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.internal.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.pm.pkg.component.flags.Flags;
31 import com.android.internal.pm.pkg.parsing.ParsingPackage;
32 import com.android.internal.pm.pkg.parsing.ParsingUtils;
33 import com.android.internal.util.CollectionUtils;
34 import com.android.internal.util.XmlUtils;
35 
36 import org.xmlpull.v1.XmlPullParser;
37 import org.xmlpull.v1.XmlPullParserException;
38 
39 import java.io.IOException;
40 import java.util.Set;
41 
42 /** @hide */
43 public class ParsedProcessUtils {
44 
45     @NonNull
parseDenyPermission(Set<String> perms, Resources res, XmlResourceParser parser, ParseInput input)46     private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
47             Resources res, XmlResourceParser parser, ParseInput input)
48             throws IOException, XmlPullParserException {
49         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
50         try {
51             String perm = sa.getNonConfigurationString(
52                     R.styleable.AndroidManifestDenyPermission_name, 0);
53             if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
54                 perms = CollectionUtils.add(perms, perm);
55             }
56         } finally {
57             sa.recycle();
58         }
59         XmlUtils.skipCurrentTag(parser);
60         return input.success(perms);
61     }
62 
63     @NonNull
parseAllowPermission(Set<String> perms, Resources res, XmlResourceParser parser, ParseInput input)64     private static ParseResult<Set<String>> parseAllowPermission(Set<String> perms, Resources res,
65             XmlResourceParser parser, ParseInput input)
66             throws IOException, XmlPullParserException {
67         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
68         try {
69             String perm = sa.getNonConfigurationString(
70                     R.styleable.AndroidManifestAllowPermission_name, 0);
71             if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
72                 perms = CollectionUtils.remove(perms, perm);
73             }
74         } finally {
75             sa.recycle();
76         }
77         XmlUtils.skipCurrentTag(parser);
78         return input.success(perms);
79     }
80 
81     @NonNull
parseProcess(Set<String> perms, String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, ParseInput input)82     private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
83             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
84             ParseInput input) throws IOException, XmlPullParserException {
85         ParsedProcessImpl proc = new ParsedProcessImpl();
86         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
87         try {
88             if (perms != null) {
89                 proc.setDeniedPermissions(new ArraySet<>(perms));
90             }
91 
92             String processName = sa.getNonConfigurationString(
93                     R.styleable.AndroidManifestProcess_process, 0);
94             ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
95                     pkg.getPackageName(), pkg.getPackageName(), processName, flags, separateProcesses,
96                     input);
97             if (processNameResult.isError()) {
98                 return input.error(processNameResult);
99             }
100 
101             String packageName = pkg.getPackageName();
102             String className = ParsingUtils.buildClassName(packageName,
103                     sa.getNonConfigurationString(R.styleable.AndroidManifestProcess_name, 0));
104 
105             proc.setName(processNameResult.getResult());
106             proc.putAppClassNameForPackage(packageName, className);
107             proc.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestProcess_gwpAsanMode, -1));
108             proc.setMemtagMode(sa.getInt(R.styleable.AndroidManifestProcess_memtagMode, -1));
109             if (sa.hasValue(R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized)) {
110                 final boolean v = sa.getBoolean(
111                         R.styleable.AndroidManifestProcess_nativeHeapZeroInitialized, false);
112                 proc.setNativeHeapZeroInitialized(
113                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
114             }
115             if (Flags.enablePerProcessUseEmbeddedDexAttr()) {
116                 proc.setUseEmbeddedDex(
117                         sa.getBoolean(R.styleable.AndroidManifestProcess_useEmbeddedDex, false));
118             } else {
119                 proc.setUseEmbeddedDex(false);
120             }
121         } finally {
122             sa.recycle();
123         }
124 
125         int type;
126         final int innerDepth = parser.getDepth();
127         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
128                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
129             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
130                 continue;
131             }
132 
133             ParseResult<?> result;
134 
135             String tagName = parser.getName();
136             switch (tagName) {
137                 case "deny-permission":
138                     ParseResult<Set<String>> denyResult = parseDenyPermission(
139                             proc.getDeniedPermissions(), res, parser, input);
140                     result = denyResult;
141                     if (denyResult.isSuccess()) {
142                         proc.setDeniedPermissions(denyResult.getResult());
143                     }
144                     break;
145                 case "allow-permission":
146                     ParseResult<Set<String>> allowResult = parseAllowPermission(
147                             proc.getDeniedPermissions(), res, parser, input);
148                     result = allowResult;
149                     if (allowResult.isSuccess()) {
150                         proc.setDeniedPermissions(allowResult.getResult());
151                     }
152                     break;
153                 default:
154                     result = ParsingUtils.unknownTag("<process>", pkg, parser, input);
155                     break;
156             }
157 
158             if (result.isError()) {
159                 return input.error(result);
160             }
161         }
162 
163         return input.success(proc);
164     }
165 
166     @NonNull
parseProcesses( String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, ParseInput input)167     public static ParseResult<ArrayMap<String, ParsedProcess>> parseProcesses(
168             String[] separateProcesses, ParsingPackage pkg, Resources res,
169             XmlResourceParser parser, int flags, ParseInput input)
170             throws IOException, XmlPullParserException {
171         Set<String> deniedPerms = null;
172         ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
173 
174         int type;
175         final int innerDepth = parser.getDepth();
176         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
177                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
178             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
179                 continue;
180             }
181 
182             ParseResult<?> result;
183 
184             String tagName = parser.getName();
185             switch (tagName) {
186                 case "deny-permission":
187                     ParseResult<Set<String>> denyResult = parseDenyPermission(deniedPerms, res,
188                             parser, input);
189                     result = denyResult;
190                     if (denyResult.isSuccess()) {
191                         deniedPerms = denyResult.getResult();
192                     }
193                     break;
194                 case "allow-permission":
195                     ParseResult<Set<String>> allowResult = parseAllowPermission(deniedPerms, res,
196                             parser, input);
197                     result = allowResult;
198                     if (allowResult.isSuccess()) {
199                         deniedPerms = allowResult.getResult();
200                     }
201                     break;
202                 case "process":
203                     ParseResult<ParsedProcess> processResult = parseProcess(deniedPerms,
204                             separateProcesses, pkg, res, parser, flags, input);
205                     result = processResult;
206                     if (processResult.isSuccess()) {
207                         ParsedProcess process = processResult.getResult();
208                         if (processes.put(process.getName(), process) != null) {
209                             result = input.error(
210                                     "<process> specified existing name '" + process.getName() + "'");
211                         }
212                     }
213                     break;
214                 default:
215                     result = ParsingUtils.unknownTag("<processes>", pkg, parser, input);
216                     break;
217             }
218 
219             if (result.isError()) {
220                 return input.error(result);
221             }
222 
223         }
224 
225         return input.success(processes);
226     }
227 }
228