• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.internal.pm.pkg.component.ComponentParseUtils.flag;
20 import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.RIGID_PARSER;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.IntentFilter;
25 import android.content.pm.PathPermission;
26 import android.content.pm.ProviderInfo;
27 import android.content.pm.parsing.result.ParseInput;
28 import android.content.pm.parsing.result.ParseResult;
29 import android.content.res.Resources;
30 import android.content.res.TypedArray;
31 import android.content.res.XmlResourceParser;
32 import android.multiuser.Flags;
33 import android.os.Build;
34 import android.os.PatternMatcher;
35 import android.util.Slog;
36 
37 import com.android.internal.R;
38 import com.android.internal.pm.pkg.parsing.ParsingPackage;
39 import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
40 import com.android.internal.pm.pkg.parsing.ParsingUtils;
41 
42 import org.xmlpull.v1.XmlPullParser;
43 import org.xmlpull.v1.XmlPullParserException;
44 
45 import java.io.IOException;
46 import java.util.Objects;
47 
48 /** @hide */
49 public class ParsedProviderUtils {
50 
51     private static final String TAG = ParsingUtils.TAG;
52 
53     @NonNull
parseProvider(String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)54     public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
55             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
56             boolean useRoundIcon, @Nullable String defaultSplitName, @NonNull ParseInput input)
57             throws IOException, XmlPullParserException {
58         String authority;
59         boolean visibleToEphemeral;
60 
61         final int targetSdkVersion = pkg.getTargetSdkVersion();
62         final String packageName = pkg.getPackageName();
63         final ParsedProviderImpl provider = new ParsedProviderImpl();
64         final String tag = parser.getName();
65 
66         TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
67         try {
68             ParseResult<ParsedProviderImpl> result =
69                     ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
70                             pkg, sa, flags, useRoundIcon, defaultSplitName, input,
71                             R.styleable.AndroidManifestProvider_banner,
72                             R.styleable.AndroidManifestProvider_description,
73                             R.styleable.AndroidManifestProvider_directBootAware,
74                             R.styleable.AndroidManifestProvider_enabled,
75                             R.styleable.AndroidManifestProvider_icon,
76                             R.styleable.AndroidManifestProvider_label,
77                             R.styleable.AndroidManifestProvider_logo,
78                             R.styleable.AndroidManifestProvider_name,
79                             R.styleable.AndroidManifestProvider_process,
80                             R.styleable.AndroidManifestProvider_roundIcon,
81                             R.styleable.AndroidManifestProvider_splitName,
82                             R.styleable.AndroidManifestProvider_attributionTags,
83                             R.styleable.AndroidManifestProvider_intentMatchingFlags);
84             if (result.isError()) {
85                 return input.error(result);
86             }
87 
88             authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);
89 
90             // For compatibility, applications targeting API level 16 or lower
91             // should have their content providers exported by default, unless they
92             // specify otherwise.
93             provider.setSyncable(sa.getBoolean(
94                     R.styleable.AndroidManifestProvider_syncable, false))
95                     .setExported(sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
96                             targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1));
97 
98             String permission = sa.getNonConfigurationString(
99                     R.styleable.AndroidManifestProvider_permission, 0);
100             String readPermission = sa.getNonConfigurationString(
101                     R.styleable.AndroidManifestProvider_readPermission, 0);
102             if (readPermission == null) {
103                 readPermission = permission;
104             }
105             if (readPermission == null) {
106                 provider.setReadPermission(pkg.getPermission());
107             } else {
108                 provider.setReadPermission(readPermission);
109             }
110             String writePermission = sa.getNonConfigurationString(
111                     R.styleable.AndroidManifestProvider_writePermission, 0);
112             if (writePermission == null) {
113                 writePermission = permission;
114             }
115             if (writePermission == null) {
116                 provider.setWritePermission(pkg.getPermission());
117             } else {
118                 provider.setWritePermission(writePermission);
119             }
120 
121             provider.setGrantUriPermissions(
122                     sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false))
123                     .setForceUriPermissions(
124                             sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions,
125                                     false))
126                     .setMultiProcess(
127                             sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false))
128                     .setInitOrder(sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0))
129                     .setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SINGLE_USER,
130                             R.styleable.AndroidManifestProvider_singleUser, sa));
131 
132             if (Flags.enableSystemUserOnlyForServicesAndProviders()) {
133                 provider.setFlags(provider.getFlags() | flag(ProviderInfo.FLAG_SYSTEM_USER_ONLY,
134                         R.styleable.AndroidManifestProvider_systemUserOnly, sa));
135             }
136             visibleToEphemeral = sa.getBoolean(
137                     R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
138             if (visibleToEphemeral) {
139                 provider.setFlags(provider.getFlags() | ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP);
140                 pkg.setVisibleToInstantApps(true);
141             }
142         } finally {
143             sa.recycle();
144         }
145 
146         if (pkg.isSaveStateDisallowed()) {
147             // A heavy-weight application can not have providers in its main process
148             if (Objects.equals(provider.getProcessName(), packageName)) {
149                 return input.error("Heavy-weight applications can not have providers"
150                         + " in main process");
151             }
152         }
153 
154         if (authority == null) {
155             return input.error("<provider> does not include authorities attribute");
156         }
157         if (authority.length() <= 0) {
158             return input.error("<provider> has empty authorities attribute");
159         }
160         provider.setAuthority(authority);
161 
162         return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input);
163     }
164 
165     @NonNull
parseProviderTags(ParsingPackage pkg, String tag, Resources res, XmlResourceParser parser, boolean visibleToEphemeral, ParsedProviderImpl provider, ParseInput input)166     private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
167             Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
168             ParsedProviderImpl provider, ParseInput input)
169             throws XmlPullParserException, IOException {
170         final int depth = parser.getDepth();
171         int type;
172         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
173                 && (type != XmlPullParser.END_TAG
174                 || parser.getDepth() > depth)) {
175             if (type != XmlPullParser.START_TAG) {
176                 continue;
177             }
178             if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement(pkg, parser)) {
179                 continue;
180             }
181 
182             String name = parser.getName();
183             final ParseResult result;
184             switch (name) {
185                 case "intent-filter":
186                     ParseResult<ParsedIntentInfoImpl> intentResult = ParsedMainComponentUtils
187                             .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
188                                     true /*allowGlobs*/, false /*allowAutoVerify*/,
189                                     false /*allowImplicitEphemeralVisibility*/,
190                                     false /*failOnNoActions*/, input);
191                     result = intentResult;
192                     if (intentResult.isSuccess()) {
193                         ParsedIntentInfoImpl intent = intentResult.getResult();
194                         IntentFilter intentFilter = intent.getIntentFilter();
195                         provider.setOrder(Math.max(intentFilter.getOrder(), provider.getOrder()));
196                         provider.addIntent(intent);
197                     }
198                     break;
199                 case "meta-data":
200                     result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
201                     break;
202                 case "property":
203                     result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input);
204                     break;
205                 case "grant-uri-permission": {
206                     result = parseGrantUriPermission(provider, pkg, res, parser, input);
207                     break;
208                 }
209                 case "path-permission": {
210                     result = parsePathPermission(provider, pkg, res, parser, input);
211                     break;
212                 }
213                 default:
214                     result = ParsingUtils.unknownTag(tag, pkg, parser, input);
215                     break;
216             }
217 
218             if (result.isError()) {
219                 return input.error(result);
220             }
221         }
222 
223         return input.success(provider);
224     }
225 
226     @NonNull
parseGrantUriPermission(ParsedProviderImpl provider, ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input)227     private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProviderImpl provider,
228             ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
229         TypedArray sa = resources.obtainAttributes(parser,
230                 R.styleable.AndroidManifestGrantUriPermission);
231         try {
232             String name = parser.getName();
233             // Pattern has priority over pre/suffix over literal path
234             PatternMatcher pa = null;
235             String str = sa.getNonConfigurationString(
236                     R.styleable.AndroidManifestGrantUriPermission_pathAdvancedPattern, 0);
237             if (str != null) {
238                 pa = new PatternMatcher(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
239             } else {
240                 str = sa.getNonConfigurationString(
241                         R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
242                 if (str != null) {
243                     pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
244                 } else {
245                     str = sa.getNonConfigurationString(
246                             R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
247                     if (str != null) {
248                         pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
249                     } else {
250                         str = sa.getNonConfigurationString(
251                                 R.styleable.AndroidManifestGrantUriPermission_pathSuffix, 0);
252                         if (str != null) {
253                             pa = new PatternMatcher(str, PatternMatcher.PATTERN_SUFFIX);
254                         } else {
255                             str = sa.getNonConfigurationString(
256                                     R.styleable.AndroidManifestGrantUriPermission_path, 0);
257                             if (str != null) {
258                                 pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
259                             }
260                         }
261                     }
262                 }
263             }
264 
265             if (pa != null) {
266                 provider.addUriPermissionPattern(pa);
267                 provider.setGrantUriPermissions(true);
268             } else {
269                 if (RIGID_PARSER) {
270                     return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
271                 }
272 
273                 Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at "
274                         + pkg.getBaseApkPath() + " " + parser.getPositionDescription());
275             }
276 
277             return input.success(provider);
278         } finally {
279             sa.recycle();
280         }
281     }
282 
283     @NonNull
parsePathPermission(ParsedProviderImpl provider, ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input)284     private static ParseResult<ParsedProvider> parsePathPermission(ParsedProviderImpl provider,
285             ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
286         TypedArray sa = resources.obtainAttributes(parser,
287                 R.styleable.AndroidManifestPathPermission);
288         try {
289             String name = parser.getName();
290 
291             String permission = sa.getNonConfigurationString(
292                     R.styleable.AndroidManifestPathPermission_permission, 0);
293             String readPermission = sa.getNonConfigurationString(
294                     R.styleable.AndroidManifestPathPermission_readPermission, 0);
295             if (readPermission == null) {
296                 readPermission = permission;
297             }
298             String writePermission = sa.getNonConfigurationString(
299                     R.styleable.AndroidManifestPathPermission_writePermission, 0);
300             if (writePermission == null) {
301                 writePermission = permission;
302             }
303 
304             boolean havePerm = false;
305             if (readPermission != null) {
306                 readPermission = readPermission.intern();
307                 havePerm = true;
308             }
309             if (writePermission != null) {
310                 writePermission = writePermission.intern();
311                 havePerm = true;
312             }
313 
314             if (!havePerm) {
315                 if (RIGID_PARSER) {
316                     return input.error(
317                             "No readPermission or writePermission for <path-permission>");
318                 }
319                 Slog.w(TAG, "No readPermission or writePermission for <path-permission>: "
320                         + name + " at " + pkg.getBaseApkPath() + " "
321                         + parser.getPositionDescription());
322                 return input.success(provider);
323             }
324 
325             // Advanced has priority over simply over prefix over literal
326             PathPermission pa = null;
327             String path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
328             if (path != null) {
329                 pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission,
330                         writePermission);
331             } else {
332                 path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathPattern, 0);
333                 if (path != null) {
334                     pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB,
335                             readPermission, writePermission);
336                 } else {
337                     path = sa.getNonConfigurationString(
338                             R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
339                     if (path != null) {
340                         pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission,
341                                 writePermission);
342                     } else {
343                         path = sa.getNonConfigurationString(
344                                 R.styleable.AndroidManifestPathPermission_pathSuffix, 0);
345                         if (path != null) {
346                             pa = new PathPermission(path, PatternMatcher.PATTERN_SUFFIX,
347                                     readPermission, writePermission);
348                         } else {
349                             path = sa.getNonConfigurationString(
350                                     R.styleable.AndroidManifestPathPermission_path, 0);
351                             if (path != null) {
352                                 pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
353                                         readPermission, writePermission);
354                             }
355                         }
356                     }
357                 }
358             }
359 
360             if (pa != null) {
361                 provider.addPathPermission(pa);
362             } else {
363                 if (RIGID_PARSER) {
364                     return input.error(
365                             "No path, pathPrefix, or pathPattern for <path-permission>");
366                 }
367 
368                 Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
369                         + name + " at " + pkg.getBaseApkPath()
370                         + " "
371                         + parser.getPositionDescription());
372             }
373 
374             return input.success(provider);
375         } finally {
376             sa.recycle();
377         }
378     }
379 }
380