1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.pm.pkg.component;
18 
19 import android.annotation.NonNull;
20 import android.content.pm.ApplicationInfo;
21 import android.content.pm.parsing.result.ParseInput;
22 import android.content.pm.parsing.result.ParseResult;
23 import android.content.res.Resources;
24 import android.content.res.TypedArray;
25 import android.content.res.XmlResourceParser;
26 import android.util.ArrayMap;
27 import android.util.ArraySet;
28 
29 import com.android.internal.R;
30 import com.android.internal.util.CollectionUtils;
31 import com.android.internal.util.XmlUtils;
32 import com.android.server.pm.pkg.parsing.ParsingPackage;
33 import com.android.server.pm.pkg.parsing.ParsingUtils;
34 
35 import org.xmlpull.v1.XmlPullParser;
36 import org.xmlpull.v1.XmlPullParserException;
37 
38 import java.io.IOException;
39 import java.util.Set;
40 
41 /** @hide */
42 public class ParsedProcessUtils {
43 
44     @NonNull
parseDenyPermission(Set<String> perms, Resources res, XmlResourceParser parser, ParseInput input)45     private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
46             Resources res, XmlResourceParser parser, ParseInput input)
47             throws IOException, XmlPullParserException {
48         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
49         try {
50             String perm = sa.getNonConfigurationString(
51                     R.styleable.AndroidManifestDenyPermission_name, 0);
52             if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
53                 perms = CollectionUtils.add(perms, perm);
54             }
55         } finally {
56             sa.recycle();
57         }
58         XmlUtils.skipCurrentTag(parser);
59         return input.success(perms);
60     }
61 
62     @NonNull
parseAllowPermission(Set<String> perms, Resources res, XmlResourceParser parser, ParseInput input)63     private static ParseResult<Set<String>> parseAllowPermission(Set<String> perms, Resources res,
64             XmlResourceParser parser, ParseInput input)
65             throws IOException, XmlPullParserException {
66         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
67         try {
68             String perm = sa.getNonConfigurationString(
69                     R.styleable.AndroidManifestAllowPermission_name, 0);
70             if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
71                 perms = CollectionUtils.remove(perms, perm);
72             }
73         } finally {
74             sa.recycle();
75         }
76         XmlUtils.skipCurrentTag(parser);
77         return input.success(perms);
78     }
79 
80     @NonNull
parseProcess(Set<String> perms, String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, ParseInput input)81     private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
82             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
83             ParseInput input) throws IOException, XmlPullParserException {
84         ParsedProcessImpl proc = new ParsedProcessImpl();
85         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
86         try {
87             if (perms != null) {
88                 proc.setDeniedPermissions(new ArraySet<>(perms));
89             }
90 
91             String processName = sa.getNonConfigurationString(
92                     R.styleable.AndroidManifestProcess_process, 0);
93             ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
94                     pkg.getPackageName(), pkg.getPackageName(), processName, flags, separateProcesses,
95                     input);
96             if (processNameResult.isError()) {
97                 return input.error(processNameResult);
98             }
99 
100             String packageName = pkg.getPackageName();
101             String className = ParsingUtils.buildClassName(packageName,
102                     sa.getNonConfigurationString(R.styleable.AndroidManifestProcess_name, 0));
103 
104             proc.setName(processNameResult.getResult());
105             proc.putAppClassNameForPackage(packageName, className);
106             proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
107             proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
108             if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
109                 final boolean v = sa.getBoolean(
110                         R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
111                 proc.setNativeHeapZeroInitialized(
112                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
113             }
114         } finally {
115             sa.recycle();
116         }
117 
118         int type;
119         final int innerDepth = parser.getDepth();
120         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
121                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
122             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
123                 continue;
124             }
125 
126             ParseResult<?> result;
127 
128             String tagName = parser.getName();
129             switch (tagName) {
130                 case "deny-permission":
131                     ParseResult<Set<String>> denyResult = parseDenyPermission(
132                             proc.getDeniedPermissions(), res, parser, input);
133                     result = denyResult;
134                     if (denyResult.isSuccess()) {
135                         proc.setDeniedPermissions(denyResult.getResult());
136                     }
137                     break;
138                 case "allow-permission":
139                     ParseResult<Set<String>> allowResult = parseAllowPermission(
140                             proc.getDeniedPermissions(), res, parser, input);
141                     result = allowResult;
142                     if (allowResult.isSuccess()) {
143                         proc.setDeniedPermissions(allowResult.getResult());
144                     }
145                     break;
146                 default:
147                     result = ParsingUtils.unknownTag("<process>", pkg, parser, input);
148                     break;
149             }
150 
151             if (result.isError()) {
152                 return input.error(result);
153             }
154         }
155 
156         return input.success(proc);
157     }
158 
159     @NonNull
parseProcesses( String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, ParseInput input)160     public static ParseResult<ArrayMap<String, ParsedProcess>> parseProcesses(
161             String[] separateProcesses, ParsingPackage pkg, Resources res,
162             XmlResourceParser parser, int flags, ParseInput input)
163             throws IOException, XmlPullParserException {
164         Set<String> deniedPerms = null;
165         ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
166 
167         int type;
168         final int innerDepth = parser.getDepth();
169         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
170                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
171             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
172                 continue;
173             }
174 
175             ParseResult<?> result;
176 
177             String tagName = parser.getName();
178             switch (tagName) {
179                 case "deny-permission":
180                     ParseResult<Set<String>> denyResult = parseDenyPermission(deniedPerms, res,
181                             parser, input);
182                     result = denyResult;
183                     if (denyResult.isSuccess()) {
184                         deniedPerms = denyResult.getResult();
185                     }
186                     break;
187                 case "allow-permission":
188                     ParseResult<Set<String>> allowResult = parseAllowPermission(deniedPerms, res,
189                             parser, input);
190                     result = allowResult;
191                     if (allowResult.isSuccess()) {
192                         deniedPerms = allowResult.getResult();
193                     }
194                     break;
195                 case "process":
196                     ParseResult<ParsedProcess> processResult = parseProcess(deniedPerms,
197                             separateProcesses, pkg, res, parser, flags, input);
198                     result = processResult;
199                     if (processResult.isSuccess()) {
200                         ParsedProcess process = processResult.getResult();
201                         if (processes.put(process.getName(), process) != null) {
202                             result = input.error(
203                                     "<process> specified existing name '" + process.getName() + "'");
204                         }
205                     }
206                     break;
207                 default:
208                     result = ParsingUtils.unknownTag("<processes>", pkg, parser, input);
209                     break;
210             }
211 
212             if (result.isError()) {
213                 return input.error(result);
214             }
215 
216         }
217 
218         return input.success(processes);
219     }
220 }
221