blob: 29ece4924e5c276be73b31685d646bc5dc958949 [file] [log] [blame]
Winson14ff7172019-10-23 10:42:27 -07001/*
Winsonf00c7552020-01-28 12:52:01 -08002 * Copyright (C) 2020 The Android Open Source Project
Winson14ff7172019-10-23 10:42:27 -07003 *
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
17package android.content.pm.parsing;
18
19import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
Winson14ff7172019-10-23 10:42:27 -070020import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
Winson14ff7172019-10-23 10:42:27 -070021import static android.content.pm.PackageManager.FEATURE_WATCH;
22import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
23import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
Rhed Jao269616b2020-03-30 13:23:14 +080024import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
Ryan Mitchell192400c2020-04-02 09:54:23 -070025import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
Winson14ff7172019-10-23 10:42:27 -070026import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
Winsone23ae202020-01-24 11:56:44 -080027import static android.os.Build.VERSION_CODES.DONUT;
Winson14ff7172019-10-23 10:42:27 -070028import static android.os.Build.VERSION_CODES.O;
29import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
Winson14ff7172019-10-23 10:42:27 -070030
Winsonf00c7552020-01-28 12:52:01 -080031import android.annotation.AnyRes;
32import android.annotation.IntRange;
Winson14ff7172019-10-23 10:42:27 -070033import android.annotation.NonNull;
34import android.annotation.Nullable;
Winsonf00c7552020-01-28 12:52:01 -080035import android.annotation.StyleableRes;
Winson14ff7172019-10-23 10:42:27 -070036import android.app.ActivityThread;
37import android.content.Intent;
38import android.content.IntentFilter;
Winson14ff7172019-10-23 10:42:27 -070039import android.content.pm.ApplicationInfo;
40import android.content.pm.ConfigurationInfo;
41import android.content.pm.FeatureGroupInfo;
42import android.content.pm.FeatureInfo;
43import android.content.pm.PackageInfo;
Winson14ff7172019-10-23 10:42:27 -070044import android.content.pm.PackageManager;
45import android.content.pm.PackageParser;
46import android.content.pm.PackageParser.PackageParserException;
47import android.content.pm.PackageParser.SigningDetails;
48import android.content.pm.Signature;
Winsonf00c7552020-01-28 12:52:01 -080049import android.content.pm.parsing.component.ComponentParseUtils;
50import android.content.pm.parsing.component.ParsedActivity;
51import android.content.pm.parsing.component.ParsedActivityUtils;
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -080052import android.content.pm.parsing.component.ParsedAttribution;
53import android.content.pm.parsing.component.ParsedAttributionUtils;
Winsonf00c7552020-01-28 12:52:01 -080054import android.content.pm.parsing.component.ParsedInstrumentation;
55import android.content.pm.parsing.component.ParsedInstrumentationUtils;
56import android.content.pm.parsing.component.ParsedIntentInfo;
57import android.content.pm.parsing.component.ParsedIntentInfoUtils;
58import android.content.pm.parsing.component.ParsedMainComponent;
59import android.content.pm.parsing.component.ParsedPermission;
60import android.content.pm.parsing.component.ParsedPermissionGroup;
61import android.content.pm.parsing.component.ParsedPermissionUtils;
62import android.content.pm.parsing.component.ParsedProcess;
63import android.content.pm.parsing.component.ParsedProcessUtils;
64import android.content.pm.parsing.component.ParsedProvider;
65import android.content.pm.parsing.component.ParsedProviderUtils;
66import android.content.pm.parsing.component.ParsedService;
67import android.content.pm.parsing.component.ParsedServiceUtils;
68import android.content.pm.parsing.result.ParseInput;
Winson727da642020-03-10 15:25:32 -070069import android.content.pm.parsing.result.ParseInput.DeferredError;
Winsonf00c7552020-01-28 12:52:01 -080070import android.content.pm.parsing.result.ParseResult;
Winson727da642020-03-10 15:25:32 -070071import android.content.pm.parsing.result.ParseTypeImpl;
Winson14ff7172019-10-23 10:42:27 -070072import android.content.pm.permission.SplitPermissionInfoParcelable;
73import android.content.pm.split.DefaultSplitAssetLoader;
74import android.content.pm.split.SplitAssetDependencyLoader;
75import android.content.pm.split.SplitAssetLoader;
Winson Chiu2fdaf812019-12-13 20:01:15 +000076import android.content.res.ApkAssets;
Winson14ff7172019-10-23 10:42:27 -070077import android.content.res.AssetManager;
78import android.content.res.Configuration;
79import android.content.res.Resources;
80import android.content.res.TypedArray;
81import android.content.res.XmlResourceParser;
82import android.net.Uri;
83import android.os.Build;
84import android.os.Bundle;
85import android.os.FileUtils;
86import android.os.RemoteException;
87import android.os.SystemProperties;
88import android.os.Trace;
Anton Hanssonb2f709d2020-01-09 10:25:23 +000089import android.os.ext.SdkExtensions;
Winson14ff7172019-10-23 10:42:27 -070090import android.text.TextUtils;
91import android.util.ArrayMap;
92import android.util.ArraySet;
93import android.util.AttributeSet;
94import android.util.DisplayMetrics;
95import android.util.Pair;
96import android.util.Slog;
97import android.util.SparseArray;
Oli Lan72ae4472020-04-14 16:50:41 +010098import android.util.SparseIntArray;
Winson14ff7172019-10-23 10:42:27 -070099import android.util.TypedValue;
100import android.util.apk.ApkSignatureVerifier;
101
102import com.android.internal.R;
103import com.android.internal.os.ClassLoaderFactory;
104import com.android.internal.util.ArrayUtils;
105import com.android.internal.util.XmlUtils;
106
107import libcore.io.IoUtils;
108import libcore.util.EmptyArray;
109
110import org.xmlpull.v1.XmlPullParser;
111import org.xmlpull.v1.XmlPullParserException;
112
113import java.io.File;
114import java.io.IOException;
115import java.security.PublicKey;
116import java.util.ArrayList;
117import java.util.Arrays;
118import java.util.List;
Winson Chiu2fdaf812019-12-13 20:01:15 +0000119import java.util.Map;
Winson14ff7172019-10-23 10:42:27 -0700120import java.util.Set;
Patrick Baumann99181232020-01-28 10:55:25 -0800121import java.util.StringTokenizer;
Winson14ff7172019-10-23 10:42:27 -0700122
Winsonf00c7552020-01-28 12:52:01 -0800123/**
124 * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
125 * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
126 *
127 * @hide
128 */
Winsone23ae202020-01-24 11:56:44 -0800129public class ParsingPackageUtils {
Winson14ff7172019-10-23 10:42:27 -0700130
Winsonf00c7552020-01-28 12:52:01 -0800131 public static final String TAG = ParsingUtils.TAG;
132
Winson727da642020-03-10 15:25:32 -0700133 /**
134 * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
135 * request, without caching the input object and without querying the internal system state
136 * for feature support.
137 */
138 @NonNull
139 public static ParseResult<ParsingPackage> parseDefaultOneTime(File file, int flags,
140 @NonNull ParseInput.Callback inputCallback, @NonNull Callback callback) {
141 if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
142 | PackageManager.MATCH_DIRECT_BOOT_AWARE)) == 0) {
143 // Caller expressed no opinion about what encryption
144 // aware/unaware components they want to see, so match both
145 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE
146 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
147 }
148
149 ParseInput input = new ParseTypeImpl(inputCallback).reset();
150 ParseResult<ParsingPackage> result;
151
152
153 ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, callback);
154 try {
155 result = parser.parsePackage(input, file, flags);
156 if (result.isError()) {
157 return result;
158 }
159 } catch (PackageParser.PackageParserException e) {
160 return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
161 "Error parsing package", e);
162 }
163
164 try {
165 ParsingPackage pkg = result.getResult();
166 if ((flags & PackageManager.GET_SIGNATURES) != 0
167 || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
168 ParsingPackageUtils.collectCertificates(pkg, false /* skipVerify */);
169 }
170
171 return input.success(pkg);
172 } catch (PackageParser.PackageParserException e) {
173 return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
174 "Error collecting package certificates", e);
175 }
176 }
177
Winsonf00c7552020-01-28 12:52:01 -0800178 private boolean mOnlyCoreApps;
179 private String[] mSeparateProcesses;
180 private DisplayMetrics mDisplayMetrics;
181 private Callback mCallback;
182
183 public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
184 DisplayMetrics displayMetrics, @NonNull Callback callback) {
185 mOnlyCoreApps = onlyCoreApps;
186 mSeparateProcesses = separateProcesses;
187 mDisplayMetrics = displayMetrics;
188 mCallback = callback;
189 }
Winson14ff7172019-10-23 10:42:27 -0700190
191 /**
192 * Parse the package at the given location. Automatically detects if the
193 * package is a monolithic style (single APK file) or cluster style
194 * (directory of APKs).
195 * <p>
196 * This performs sanity checking on cluster style packages, such as
197 * requiring identical package name and version codes, a single base APK,
198 * and unique split names.
199 * <p>
200 * Note that this <em>does not</em> perform signature verification; that
Winsone23ae202020-01-24 11:56:44 -0800201 * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
Winson14ff7172019-10-23 10:42:27 -0700202 *
203 * If {@code useCaches} is true, the package parser might return a cached
204 * result from a previous parse of the same {@code packageFile} with the same
205 * {@code flags}. Note that this method does not check whether {@code packageFile}
206 * has changed since the last parse, it's up to callers to do so.
207 *
208 * @see PackageParser#parsePackageLite(File, int)
209 */
Winsonf00c7552020-01-28 12:52:01 -0800210 public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
211 int flags)
212 throws PackageParserException {
Winson14ff7172019-10-23 10:42:27 -0700213 if (packageFile.isDirectory()) {
Winsonf00c7552020-01-28 12:52:01 -0800214 return parseClusterPackage(input, packageFile, flags);
Winson14ff7172019-10-23 10:42:27 -0700215 } else {
Winsonf00c7552020-01-28 12:52:01 -0800216 return parseMonolithicPackage(input, packageFile, flags);
Winson14ff7172019-10-23 10:42:27 -0700217 }
218 }
219
220 /**
221 * Parse all APKs contained in the given directory, treating them as a
222 * single package. This also performs sanity checking, such as requiring
223 * identical package name and version codes, a single base APK, and unique
224 * split names.
225 * <p>
226 * Note that this <em>does not</em> perform signature verification; that
Winsone23ae202020-01-24 11:56:44 -0800227 * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
Winson14ff7172019-10-23 10:42:27 -0700228 */
Winsonf00c7552020-01-28 12:52:01 -0800229 private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
230 int flags) throws PackageParserException {
Winson14ff7172019-10-23 10:42:27 -0700231 final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
232 0);
Winsonf00c7552020-01-28 12:52:01 -0800233 if (mOnlyCoreApps && !lite.coreApp) {
Rhed Jao269616b2020-03-30 13:23:14 +0800234 return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
235 "Not a coreApp: " + packageDir);
Winson14ff7172019-10-23 10:42:27 -0700236 }
237
238 // Build the split dependency tree.
239 SparseArray<int[]> splitDependencies = null;
240 final SplitAssetLoader assetLoader;
241 if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
242 try {
243 splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
244 assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
245 } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
Winsonf00c7552020-01-28 12:52:01 -0800246 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
Winson14ff7172019-10-23 10:42:27 -0700247 }
248 } else {
249 assetLoader = new DefaultSplitAssetLoader(lite, flags);
250 }
251
252 try {
253 final AssetManager assets = assetLoader.getBaseAssetManager();
254 final File baseApk = new File(lite.baseCodePath);
Winsonf00c7552020-01-28 12:52:01 -0800255 ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
256 lite.codePath, assets, flags);
Winsonf00c7552020-01-28 12:52:01 -0800257 if (result.isError()) {
Winson36c44702020-04-09 13:48:45 -0700258 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -0700259 }
260
Winsonf00c7552020-01-28 12:52:01 -0800261 ParsingPackage pkg = result.getResult();
Winson14ff7172019-10-23 10:42:27 -0700262 if (!ArrayUtils.isEmpty(lite.splitNames)) {
Winsonf00c7552020-01-28 12:52:01 -0800263 pkg.asSplit(
Winson14ff7172019-10-23 10:42:27 -0700264 lite.splitNames,
265 lite.splitCodePaths,
266 lite.splitRevisionCodes,
267 splitDependencies
268 );
269 final int num = lite.splitNames.length;
270
271 for (int i = 0; i < num; i++) {
272 final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
Winsonf00c7552020-01-28 12:52:01 -0800273 parseSplitApk(input, pkg, i, splitAssets, flags);
Winson14ff7172019-10-23 10:42:27 -0700274 }
275 }
276
Winsonf00c7552020-01-28 12:52:01 -0800277 pkg.setUse32BitAbi(lite.use32bitAbi);
278 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700279 } finally {
280 IoUtils.closeQuietly(assetLoader);
281 }
282 }
283
284 /**
285 * Parse the given APK file, treating it as as a single monolithic package.
286 * <p>
287 * Note that this <em>does not</em> perform signature verification; that
Winsone23ae202020-01-24 11:56:44 -0800288 * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
Winson14ff7172019-10-23 10:42:27 -0700289 */
Winsonf00c7552020-01-28 12:52:01 -0800290 private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
291 int flags) throws PackageParserException {
Winson14ff7172019-10-23 10:42:27 -0700292 final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
293 flags);
Winsonf00c7552020-01-28 12:52:01 -0800294 if (mOnlyCoreApps && !lite.coreApp) {
Rhed Jao269616b2020-03-30 13:23:14 +0800295 return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
296 "Not a coreApp: " + apkFile);
Winson14ff7172019-10-23 10:42:27 -0700297 }
298
299 final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
300 try {
Winsonf00c7552020-01-28 12:52:01 -0800301 ParseResult<ParsingPackage> result = parseBaseApk(input,
302 apkFile,
303 apkFile.getCanonicalPath(),
304 assetLoader.getBaseAssetManager(), flags);
305 if (result.isError()) {
306 return input.error(result);
307 }
308
309 return input.success(result.getResult()
310 .setUse32BitAbi(lite.use32bitAbi));
Winson14ff7172019-10-23 10:42:27 -0700311 } catch (IOException e) {
Winsonf00c7552020-01-28 12:52:01 -0800312 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
Winson14ff7172019-10-23 10:42:27 -0700313 "Failed to get path: " + apkFile, e);
314 } finally {
315 IoUtils.closeQuietly(assetLoader);
316 }
317 }
318
Winsonf00c7552020-01-28 12:52:01 -0800319 private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
320 String codePath, AssetManager assets, int flags) {
Winson14ff7172019-10-23 10:42:27 -0700321 final String apkPath = apkFile.getAbsolutePath();
322
323 String volumeUuid = null;
324 if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
325 final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
326 volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
327 }
328
329 if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
330
Winsonf00c7552020-01-28 12:52:01 -0800331 final int cookie = assets.findCookieForPath(apkPath);
332 if (cookie == 0) {
333 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
334 "Failed adding asset path: " + apkPath);
335 }
Winson14ff7172019-10-23 10:42:27 -0700336
Winsonf00c7552020-01-28 12:52:01 -0800337 try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
338 PackageParser.ANDROID_MANIFEST_FILENAME)) {
339 final Resources res = new Resources(assets, mDisplayMetrics, null);
340
341 ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
342 parser, flags);
343 if (result.isError()) {
344 return input.error(result.getErrorCode(),
Winson14ff7172019-10-23 10:42:27 -0700345 apkPath + " (at " + parser.getPositionDescription() + "): "
346 + result.getErrorMessage());
347 }
348
Ryan Mitchell192400c2020-04-02 09:54:23 -0700349 final ParsingPackage pkg = result.getResult();
350 if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.R
351 && assets.containsAllocatedTable()) {
352 final ParseResult<?> deferResult = input.deferError(
353 "Targeting R+ (version" + Build.VERSION_CODES.R + " and above) requires the"
354 + " resources.arsc of installed APKs to be stored uncompressed and"
355 + " aligned on a 4-byte boundary",
356 DeferredError.RESOURCES_ARSC_COMPRESSED);
357 if (deferResult.isError()) {
358 return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
359 deferResult.getErrorMessage());
360 }
361 }
362
Winson Chiu2fdaf812019-12-13 20:01:15 +0000363 ApkAssets apkAssets = assets.getApkAssets()[0];
364 if (apkAssets.definesOverlayable()) {
365 SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
366 int size = packageNames.size();
367 for (int index = 0; index < size; index++) {
368 String packageName = packageNames.get(index);
369 Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
370 if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
371 for (String overlayable : overlayableToActor.keySet()) {
372 pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
373 }
374 }
375 }
376 }
377
Winsonf00c7552020-01-28 12:52:01 -0800378 pkg.setVolumeUuid(volumeUuid)
Winson14ff7172019-10-23 10:42:27 -0700379 .setSigningDetails(SigningDetails.UNKNOWN);
Winsonf00c7552020-01-28 12:52:01 -0800380
381 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700382 } catch (Exception e) {
Winsonf00c7552020-01-28 12:52:01 -0800383 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
Winson14ff7172019-10-23 10:42:27 -0700384 "Failed to read manifest from " + apkPath, e);
Winson14ff7172019-10-23 10:42:27 -0700385 }
386 }
387
Winsonf00c7552020-01-28 12:52:01 -0800388 private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
389 ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
390 final String apkPath = pkg.getSplitCodePaths()[splitIndex];
Winson14ff7172019-10-23 10:42:27 -0700391
392 if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
393
Winsonf00c7552020-01-28 12:52:01 -0800394 // This must always succeed, as the path has been added to the AssetManager before.
395 final int cookie = assets.findCookieForPath(apkPath);
396 if (cookie == 0) {
397 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
398 "Failed adding asset path: " + apkPath);
399 }
400 try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
401 PackageParser.ANDROID_MANIFEST_FILENAME)) {
402 Resources res = new Resources(assets, mDisplayMetrics, null);
403 ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
404 parser, flags, splitIndex);
405 if (parseResult.isError()) {
406 return input.error(parseResult.getErrorCode(),
Winson14ff7172019-10-23 10:42:27 -0700407 apkPath + " (at " + parser.getPositionDescription() + "): "
408 + parseResult.getErrorMessage());
409 }
Winsonf00c7552020-01-28 12:52:01 -0800410
411 return parseResult;
Winson14ff7172019-10-23 10:42:27 -0700412 } catch (Exception e) {
Winsonf00c7552020-01-28 12:52:01 -0800413 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
Winson14ff7172019-10-23 10:42:27 -0700414 "Failed to read manifest from " + apkPath, e);
Winson14ff7172019-10-23 10:42:27 -0700415 }
416 }
417
418 /**
419 * Parse the manifest of a <em>base APK</em>. When adding new features you
420 * need to consider whether they should be supported by split APKs and child
421 * packages.
422 *
Winsonf00c7552020-01-28 12:52:01 -0800423 * @param apkPath The package apk file path
424 * @param res The resources from which to resolve values
425 * @param parser The manifest parser
426 * @param flags Flags how to parse
Winson14ff7172019-10-23 10:42:27 -0700427 * @return Parsed package or null on error.
428 */
Winsonf00c7552020-01-28 12:52:01 -0800429 private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
430 String codePath, Resources res, XmlResourceParser parser, int flags)
431 throws XmlPullParserException, IOException {
Winson14ff7172019-10-23 10:42:27 -0700432 final String splitName;
433 final String pkgName;
434
435 try {
436 Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser,
437 parser);
438 pkgName = packageSplit.first;
439 splitName = packageSplit.second;
440
441 if (!TextUtils.isEmpty(splitName)) {
Winsonf00c7552020-01-28 12:52:01 -0800442 return input.error(
Winson14ff7172019-10-23 10:42:27 -0700443 PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
444 "Expected base APK, but found split " + splitName
445 );
446 }
447 } catch (PackageParserException e) {
Winsonf00c7552020-01-28 12:52:01 -0800448 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
Winson14ff7172019-10-23 10:42:27 -0700449 }
450
Todd Kennedy83eddae2020-03-02 09:21:25 -0800451 final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
Winson14ff7172019-10-23 10:42:27 -0700452 try {
Todd Kennedy83eddae2020-03-02 09:21:25 -0800453 final boolean isCoreApp =
454 parser.getAttributeBooleanValue(null, "coreApp", false);
455 final ParsingPackage pkg = mCallback.startParsingPackage(
456 pkgName, apkPath, codePath, manifestArray, isCoreApp);
457 final ParseResult<ParsingPackage> result =
458 parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
Winsonf00c7552020-01-28 12:52:01 -0800459 if (result.isError()) {
Winson14ff7172019-10-23 10:42:27 -0700460 return result;
461 }
462
Winsonf00c7552020-01-28 12:52:01 -0800463 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700464 } finally {
Winsonf00c7552020-01-28 12:52:01 -0800465 manifestArray.recycle();
Winson14ff7172019-10-23 10:42:27 -0700466 }
467 }
468
469 /**
470 * Parse the manifest of a <em>split APK</em>.
471 * <p>
472 * Note that split APKs have many more restrictions on what they're capable
473 * of doing, so many valid features of a base APK have been carefully
474 * omitted here.
475 *
Winsonf00c7552020-01-28 12:52:01 -0800476 * @param pkg builder to fill
Winson14ff7172019-10-23 10:42:27 -0700477 * @return false on failure
478 */
Winsonf00c7552020-01-28 12:52:01 -0800479 private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
480 Resources res, XmlResourceParser parser, int flags, int splitIndex)
481 throws XmlPullParserException, IOException, PackageParserException {
Winson14ff7172019-10-23 10:42:27 -0700482 AttributeSet attrs = parser;
483
484 // We parsed manifest tag earlier; just skip past it
485 PackageParser.parsePackageSplitNames(parser, attrs);
486
487 int type;
488
489 boolean foundApp = false;
490
491 int outerDepth = parser.getDepth();
Winsonf00c7552020-01-28 12:52:01 -0800492 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
493 if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -0700494 continue;
495 }
496
Winsonf00c7552020-01-28 12:52:01 -0800497 final ParseResult result;
Winson14ff7172019-10-23 10:42:27 -0700498 String tagName = parser.getName();
Winsonf00c7552020-01-28 12:52:01 -0800499 if (PackageParser.TAG_APPLICATION.equals(tagName)) {
Winson14ff7172019-10-23 10:42:27 -0700500 if (foundApp) {
501 if (PackageParser.RIGID_PARSER) {
Winsonf00c7552020-01-28 12:52:01 -0800502 result = input.error("<manifest> has more than one <application>");
Winson14ff7172019-10-23 10:42:27 -0700503 } else {
504 Slog.w(TAG, "<manifest> has more than one <application>");
Winsonf00c7552020-01-28 12:52:01 -0800505 result = input.success(null);
Winson14ff7172019-10-23 10:42:27 -0700506 }
Winsonf00c7552020-01-28 12:52:01 -0800507 } else {
508 foundApp = true;
509 result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
Winson14ff7172019-10-23 10:42:27 -0700510 }
Winson14ff7172019-10-23 10:42:27 -0700511 } else {
Winsonf00c7552020-01-28 12:52:01 -0800512 result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
513 }
514
515 if (result.isError()) {
516 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -0700517 }
518 }
519
520 if (!foundApp) {
Winson727da642020-03-10 15:25:32 -0700521 ParseResult<?> deferResult = input.deferError(
522 "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG);
523 if (deferResult.isError()) {
524 return input.error(deferResult);
525 }
Winson14ff7172019-10-23 10:42:27 -0700526 }
527
Winsonf00c7552020-01-28 12:52:01 -0800528 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700529 }
530
531 /**
532 * Parse the {@code application} XML tree at the current parse location in a
533 * <em>split APK</em> manifest.
534 * <p>
535 * Note that split APKs have many more restrictions on what they're capable
536 * of doing, so many valid features of a base APK have been carefully
537 * omitted here.
538 */
Winsonf00c7552020-01-28 12:52:01 -0800539 private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
540 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
541 throws XmlPullParserException, IOException {
Winson14ff7172019-10-23 10:42:27 -0700542 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
Winsonf00c7552020-01-28 12:52:01 -0800543 try {
544 pkg.setSplitHasCode(splitIndex, sa.getBoolean(
545 R.styleable.AndroidManifestApplication_hasCode, true));
Winson14ff7172019-10-23 10:42:27 -0700546
Winsonf00c7552020-01-28 12:52:01 -0800547 final String classLoaderName = sa.getString(
548 R.styleable.AndroidManifestApplication_classLoader);
549 if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
550 classLoaderName)) {
551 pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
552 } else {
553 return input.error("Invalid class loader name: " + classLoaderName);
554 }
555 } finally {
556 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -0700557 }
Winsonf00c7552020-01-28 12:52:01 -0800558 final int depth = parser.getDepth();
Winson14ff7172019-10-23 10:42:27 -0700559 int type;
560 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Winsonf00c7552020-01-28 12:52:01 -0800561 && (type != XmlPullParser.END_TAG
562 || parser.getDepth() > depth)) {
563 if (type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -0700564 continue;
565 }
566
Winsonf00c7552020-01-28 12:52:01 -0800567 ParsedMainComponent mainComponent = null;
Winson14ff7172019-10-23 10:42:27 -0700568
Winsonf00c7552020-01-28 12:52:01 -0800569 final ParseResult result;
Winson14ff7172019-10-23 10:42:27 -0700570 String tagName = parser.getName();
Winsonf00c7552020-01-28 12:52:01 -0800571 boolean isActivity = false;
Winson14ff7172019-10-23 10:42:27 -0700572 switch (tagName) {
573 case "activity":
Winsonf00c7552020-01-28 12:52:01 -0800574 isActivity = true;
575 // fall-through
Winson14ff7172019-10-23 10:42:27 -0700576 case "receiver":
Winsonf00c7552020-01-28 12:52:01 -0800577 ParseResult<ParsedActivity> activityResult =
578 ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
579 res,
580 parser, flags, PackageParser.sUseRoundIcon, input);
581 if (activityResult.isSuccess()) {
582 ParsedActivity activity = activityResult.getResult();
583 if (isActivity) {
584 pkg.addActivity(activity);
585 } else {
586 pkg.addReceiver(activity);
587 }
588 mainComponent = activity;
Winson14ff7172019-10-23 10:42:27 -0700589 }
Winsonf00c7552020-01-28 12:52:01 -0800590 result = activityResult;
Winson14ff7172019-10-23 10:42:27 -0700591 break;
592 case "service":
Winsonf00c7552020-01-28 12:52:01 -0800593 ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
594 mSeparateProcesses, pkg, res, parser, flags,
595 PackageParser.sUseRoundIcon, input);
596 if (serviceResult.isSuccess()) {
597 ParsedService service = serviceResult.getResult();
598 pkg.addService(service);
599 mainComponent = service;
Winson14ff7172019-10-23 10:42:27 -0700600 }
Winsonf00c7552020-01-28 12:52:01 -0800601 result = serviceResult;
Winson14ff7172019-10-23 10:42:27 -0700602 break;
603 case "provider":
Winsonf00c7552020-01-28 12:52:01 -0800604 ParseResult<ParsedProvider> providerResult =
605 ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
606 flags, PackageParser.sUseRoundIcon, input);
607 if (providerResult.isSuccess()) {
608 ParsedProvider provider = providerResult.getResult();
609 pkg.addProvider(provider);
610 mainComponent = provider;
Winson14ff7172019-10-23 10:42:27 -0700611 }
Winsonf00c7552020-01-28 12:52:01 -0800612 result = providerResult;
Winson14ff7172019-10-23 10:42:27 -0700613 break;
614 case "activity-alias":
Winsonf00c7552020-01-28 12:52:01 -0800615 activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
616 PackageParser.sUseRoundIcon, input);
617 if (activityResult.isSuccess()) {
618 ParsedActivity activity = activityResult.getResult();
619 pkg.addActivity(activity);
620 mainComponent = activity;
Winson14ff7172019-10-23 10:42:27 -0700621 }
622
Winsonf00c7552020-01-28 12:52:01 -0800623 result = activityResult;
Winson14ff7172019-10-23 10:42:27 -0700624 break;
625 default:
Winsonf00c7552020-01-28 12:52:01 -0800626 result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
627 break;
Winson14ff7172019-10-23 10:42:27 -0700628 }
629
Winsonf00c7552020-01-28 12:52:01 -0800630 if (result.isError()) {
631 return input.error(result);
632 }
633
634 if (mainComponent != null && mainComponent.getSplitName() == null) {
Winson14ff7172019-10-23 10:42:27 -0700635 // If the loaded component did not specify a split, inherit the split name
636 // based on the split it is defined in.
637 // This is used to later load the correct split when starting this
638 // component.
Winsonf00c7552020-01-28 12:52:01 -0800639 mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]);
Winson14ff7172019-10-23 10:42:27 -0700640 }
641 }
642
Winsonf00c7552020-01-28 12:52:01 -0800643 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700644 }
645
Winsonf00c7552020-01-28 12:52:01 -0800646 /**
647 * For parsing non-MainComponents. Main ones have an order and some special handling which is
648 * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
649 * XmlResourceParser, int, int)}.
650 */
651 private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
652 Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
653 switch (tag) {
654 case "meta-data":
655 // note: application meta-data is stored off to the side, so it can
656 // remain null in the primary copy (we like to avoid extra copies because
657 // it can be large)
658 ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
659 pkg.getMetaData(), input);
660 if (metaDataResult.isSuccess()) {
661 pkg.setMetaData(metaDataResult.getResult());
662 }
663 return metaDataResult;
664 case "uses-static-library":
665 return parseUsesStaticLibrary(input, pkg, res, parser);
666 case "uses-library":
667 return parseUsesLibrary(input, pkg, res, parser);
668 case "uses-package":
669 // Dependencies for app installers; we don't currently try to
670 // enforce this.
671 return input.success(null);
672 default:
673 return ParsingUtils.unknownTag("<application>", pkg, parser, input);
674 }
675 }
Winson14ff7172019-10-23 10:42:27 -0700676
Winsonf00c7552020-01-28 12:52:01 -0800677 private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
678 TypedArray sa, Resources res, XmlResourceParser parser, int flags)
679 throws XmlPullParserException, IOException {
680 ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
681 if (sharedUserResult.isError()) {
Winson14ff7172019-10-23 10:42:27 -0700682 return sharedUserResult;
683 }
684
Todd Kennedy83eddae2020-03-02 09:21:25 -0800685 pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
Winsonf00c7552020-01-28 12:52:01 -0800686 R.styleable.AndroidManifest_installLocation, sa))
Todd Kennedy83eddae2020-03-02 09:21:25 -0800687 .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
Winsonf00c7552020-01-28 12:52:01 -0800688 R.styleable.AndroidManifest_targetSandboxVersion, sa))
689 /* Set the global "on SD card" flag */
Todd Kennedy83eddae2020-03-02 09:21:25 -0800690 .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
Winson14ff7172019-10-23 10:42:27 -0700691
Winsonf00c7552020-01-28 12:52:01 -0800692 boolean foundApp = false;
693 final int depth = parser.getDepth();
694 int type;
Winson14ff7172019-10-23 10:42:27 -0700695 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Winsonf00c7552020-01-28 12:52:01 -0800696 && (type != XmlPullParser.END_TAG
697 || parser.getDepth() > depth)) {
698 if (type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -0700699 continue;
700 }
701
702 String tagName = parser.getName();
Winsonf00c7552020-01-28 12:52:01 -0800703 final ParseResult result;
Winson14ff7172019-10-23 10:42:27 -0700704
Winson14ff7172019-10-23 10:42:27 -0700705 // TODO(b/135203078): Convert to instance methods to share variables
Winsonf00c7552020-01-28 12:52:01 -0800706 // <application> has special logic, so it's handled outside the general method
707 if (PackageParser.TAG_APPLICATION.equals(tagName)) {
708 if (foundApp) {
709 if (PackageParser.RIGID_PARSER) {
710 result = input.error("<manifest> has more than one <application>");
Winson14ff7172019-10-23 10:42:27 -0700711 } else {
Winsonf00c7552020-01-28 12:52:01 -0800712 Slog.w(TAG, "<manifest> has more than one <application>");
713 result = input.success(null);
Winson14ff7172019-10-23 10:42:27 -0700714 }
Winsonf00c7552020-01-28 12:52:01 -0800715 } else {
716 foundApp = true;
717 result = parseBaseApplication(input, pkg, res, parser, flags);
718 }
719 } else {
720 result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
Winson14ff7172019-10-23 10:42:27 -0700721 }
722
Winsonf00c7552020-01-28 12:52:01 -0800723 if (result.isError()) {
724 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -0700725 }
726 }
727
Winsonf00c7552020-01-28 12:52:01 -0800728 if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
Winson727da642020-03-10 15:25:32 -0700729 ParseResult<?> deferResult = input.deferError(
730 "<manifest> does not contain an <application> or <instrumentation>",
731 DeferredError.MISSING_APP_TAG);
732 if (deferResult.isError()) {
733 return input.error(deferResult);
734 }
Winson14ff7172019-10-23 10:42:27 -0700735 }
736
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -0800737 if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) {
Winsonf00c7552020-01-28 12:52:01 -0800738 return input.error(
Philip P. Moltmann9046d822019-12-13 15:59:49 -0800739 INSTALL_PARSE_FAILED_BAD_MANIFEST,
740 "Combination <feature> tags are not valid"
741 );
742 }
743
Winsonf00c7552020-01-28 12:52:01 -0800744 convertNewPermissions(pkg);
Winson14ff7172019-10-23 10:42:27 -0700745
Winsonf00c7552020-01-28 12:52:01 -0800746 convertSplitPermissions(pkg);
Winson14ff7172019-10-23 10:42:27 -0700747
748 // At this point we can check if an application is not supporting densities and hence
749 // cannot be windowed / resized. Note that an SDK version of 0 is common for
750 // pre-Doughnut applications.
Winsonf00c7552020-01-28 12:52:01 -0800751 if (pkg.getTargetSdkVersion() < DONUT
Winsone23ae202020-01-24 11:56:44 -0800752 || (!pkg.isSupportsSmallScreens()
753 && !pkg.isSupportsNormalScreens()
754 && !pkg.isSupportsLargeScreens()
755 && !pkg.isSupportsExtraLargeScreens()
756 && !pkg.isResizeable()
Winsonf00c7552020-01-28 12:52:01 -0800757 && !pkg.isAnyDensity())) {
758 adjustPackageToBeUnresizeableAndUnpipable(pkg);
759 }
760
761 return input.success(pkg);
Winsone23ae202020-01-24 11:56:44 -0800762 }
763
Winsonf00c7552020-01-28 12:52:01 -0800764 private ParseResult parseBaseApkTag(String tag, ParseInput input,
765 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
766 throws IOException, XmlPullParserException {
767 switch (tag) {
768 case PackageParser.TAG_OVERLAY:
769 return parseOverlay(input, pkg, res, parser);
770 case PackageParser.TAG_KEY_SETS:
771 return parseKeySets(input, pkg, res, parser);
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -0800772 case "feature": // TODO moltmann: Remove
773 case PackageParser.TAG_ATTRIBUTION:
774 return parseAttribution(input, pkg, res, parser);
Winsonf00c7552020-01-28 12:52:01 -0800775 case PackageParser.TAG_PERMISSION_GROUP:
776 return parsePermissionGroup(input, pkg, res, parser);
777 case PackageParser.TAG_PERMISSION:
778 return parsePermission(input, pkg, res, parser);
779 case PackageParser.TAG_PERMISSION_TREE:
780 return parsePermissionTree(input, pkg, res, parser);
781 case PackageParser.TAG_USES_PERMISSION:
782 case PackageParser.TAG_USES_PERMISSION_SDK_M:
783 case PackageParser.TAG_USES_PERMISSION_SDK_23:
784 return parseUsesPermission(input, pkg, res, parser);
785 case PackageParser.TAG_USES_CONFIGURATION:
786 return parseUsesConfiguration(input, pkg, res, parser);
787 case PackageParser.TAG_USES_FEATURE:
788 return parseUsesFeature(input, pkg, res, parser);
789 case PackageParser.TAG_FEATURE_GROUP:
790 return parseFeatureGroup(input, pkg, res, parser);
791 case PackageParser.TAG_USES_SDK:
792 return parseUsesSdk(input, pkg, res, parser);
793 case PackageParser.TAG_SUPPORT_SCREENS:
794 return parseSupportScreens(input, pkg, res, parser);
795 case PackageParser.TAG_PROTECTED_BROADCAST:
796 return parseProtectedBroadcast(input, pkg, res, parser);
797 case PackageParser.TAG_INSTRUMENTATION:
798 return parseInstrumentation(input, pkg, res, parser);
799 case PackageParser.TAG_ORIGINAL_PACKAGE:
800 return parseOriginalPackage(input, pkg, res, parser);
801 case PackageParser.TAG_ADOPT_PERMISSIONS:
802 return parseAdoptPermissions(input, pkg, res, parser);
803 case PackageParser.TAG_USES_GL_TEXTURE:
804 case PackageParser.TAG_COMPATIBLE_SCREENS:
805 case PackageParser.TAG_SUPPORTS_INPUT:
806 case PackageParser.TAG_EAT_COMMENT:
807 // Just skip this tag
808 XmlUtils.skipCurrentTag(parser);
809 return input.success(pkg);
810 case PackageParser.TAG_RESTRICT_UPDATE:
811 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
812 case PackageParser.TAG_QUERIES:
813 return parseQueries(input, pkg, res, parser);
814 default:
815 return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
Winson14ff7172019-10-23 10:42:27 -0700816 }
817 }
818
Winsonf00c7552020-01-28 12:52:01 -0800819 private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
820 ParsingPackage pkg, TypedArray sa) {
821 String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
Winson14ff7172019-10-23 10:42:27 -0700822 if (TextUtils.isEmpty(str)) {
Winsonf00c7552020-01-28 12:52:01 -0800823 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700824 }
825
Winson727da642020-03-10 15:25:32 -0700826 if (!"android".equals(pkg.getPackageName())) {
827 ParseResult<?> nameResult = validateName(input, str, true, true);
828 if (nameResult.isError()) {
Winsonf00c7552020-01-28 12:52:01 -0800829 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
830 "<manifest> specifies bad sharedUserId name \"" + str + "\": "
831 + nameResult.getErrorMessage());
832 }
Winson14ff7172019-10-23 10:42:27 -0700833 }
834
Winsonf00c7552020-01-28 12:52:01 -0800835 return input.success(pkg
836 .setSharedUserId(str.intern())
837 .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
Winson14ff7172019-10-23 10:42:27 -0700838 }
839
Winsonf00c7552020-01-28 12:52:01 -0800840 private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
841 ParsingPackage pkg, Resources res, XmlResourceParser parser)
842 throws XmlPullParserException, IOException {
Winson14ff7172019-10-23 10:42:27 -0700843 // we've encountered the 'key-sets' tag
844 // all the keys and keysets that we want must be defined here
845 // so we're going to iterate over the parser and pull out the things we want
846 int outerDepth = parser.getDepth();
847 int currentKeySetDepth = -1;
848 int type;
849 String currentKeySet = null;
850 ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
851 ArraySet<String> upgradeKeySets = new ArraySet<>();
Winsonf00c7552020-01-28 12:52:01 -0800852 ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
Winson14ff7172019-10-23 10:42:27 -0700853 ArraySet<String> improperKeySets = new ArraySet<>();
854 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
855 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
856 if (type == XmlPullParser.END_TAG) {
857 if (parser.getDepth() == currentKeySetDepth) {
858 currentKeySet = null;
859 currentKeySetDepth = -1;
860 }
861 continue;
862 }
863 String tagName = parser.getName();
Winsonf00c7552020-01-28 12:52:01 -0800864 switch (tagName) {
865 case "key-set": {
866 if (currentKeySet != null) {
867 return input.error("Improperly nested 'key-set' tag at "
868 + parser.getPositionDescription());
Winson14ff7172019-10-23 10:42:27 -0700869 }
Winsonf00c7552020-01-28 12:52:01 -0800870 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
871 try {
872 final String keysetName = sa.getNonResourceString(
873 R.styleable.AndroidManifestKeySet_name);
874 definedKeySets.put(keysetName, new ArraySet<>());
875 currentKeySet = keysetName;
876 currentKeySetDepth = parser.getDepth();
877 } finally {
878 sa.recycle();
879 }
880 } break;
881 case "public-key": {
882 if (currentKeySet == null) {
883 return input.error("Improperly nested 'key-set' tag at "
884 + parser.getPositionDescription());
885 }
886 TypedArray sa = res.obtainAttributes(parser,
887 R.styleable.AndroidManifestPublicKey);
888 try {
889 final String publicKeyName = nonResString(
890 R.styleable.AndroidManifestPublicKey_name, sa);
891 final String encodedKey = nonResString(
892 R.styleable.AndroidManifestPublicKey_value, sa);
893 if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
894 return input.error("'public-key' " + publicKeyName
895 + " must define a public-key value on first use at "
896 + parser.getPositionDescription());
897 } else if (encodedKey != null) {
898 PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
899 if (currentKey == null) {
900 Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
901 + parser.getPositionDescription() + " key-set "
902 + currentKeySet
903 + " will not be added to the package's defined key-sets.");
904 improperKeySets.add(currentKeySet);
905 XmlUtils.skipCurrentTag(parser);
906 continue;
907 }
908 if (publicKeys.get(publicKeyName) == null
909 || publicKeys.get(publicKeyName).equals(currentKey)) {
Winson14ff7172019-10-23 10:42:27 -0700910
Winsonf00c7552020-01-28 12:52:01 -0800911 /* public-key first definition, or matches old definition */
912 publicKeys.put(publicKeyName, currentKey);
913 } else {
914 return input.error("Value of 'public-key' " + publicKeyName
Winson14ff7172019-10-23 10:42:27 -0700915 + " conflicts with previously defined value at "
Winsonf00c7552020-01-28 12:52:01 -0800916 + parser.getPositionDescription());
917 }
918 }
919 definedKeySets.get(currentKeySet).add(publicKeyName);
920 XmlUtils.skipCurrentTag(parser);
921 } finally {
922 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -0700923 }
Winsonf00c7552020-01-28 12:52:01 -0800924 } break;
925 case "upgrade-key-set": {
926 TypedArray sa = res.obtainAttributes(parser,
927 R.styleable.AndroidManifestUpgradeKeySet);
928 try {
929 String name = sa.getNonResourceString(
930 R.styleable.AndroidManifestUpgradeKeySet_name);
931 upgradeKeySets.add(name);
932 XmlUtils.skipCurrentTag(parser);
933 } finally {
934 sa.recycle();
935 }
936 } break;
937 default:
938 ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
939 input);
940 if (result.isError()) {
941 return input.error(result);
942 }
943 break;
Winson14ff7172019-10-23 10:42:27 -0700944 }
945 }
Winsonf00c7552020-01-28 12:52:01 -0800946 String packageName = pkg.getPackageName();
Winson14ff7172019-10-23 10:42:27 -0700947 Set<String> publicKeyNames = publicKeys.keySet();
948 if (publicKeyNames.removeAll(definedKeySets.keySet())) {
Winsonf00c7552020-01-28 12:52:01 -0800949 return input.error("Package" + packageName
950 + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
Winson14ff7172019-10-23 10:42:27 -0700951 }
952
953 for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
954 final String keySetName = e.getKey();
955 if (e.getValue().size() == 0) {
956 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
957 + "'key-set' " + keySetName + " has no valid associated 'public-key'."
958 + " Not including in package's defined key-sets.");
959 continue;
960 } else if (improperKeySets.contains(keySetName)) {
961 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
962 + "'key-set' " + keySetName + " contained improper 'public-key'"
963 + " tags. Not including in package's defined key-sets.");
964 continue;
965 }
966
967 for (String s : e.getValue()) {
Winsonf00c7552020-01-28 12:52:01 -0800968 pkg.addKeySet(keySetName, publicKeys.get(s));
Winson14ff7172019-10-23 10:42:27 -0700969 }
970 }
Winsonf00c7552020-01-28 12:52:01 -0800971 if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
972 pkg.setUpgradeKeySets(upgradeKeySets);
Winson14ff7172019-10-23 10:42:27 -0700973 } else {
Winsonf00c7552020-01-28 12:52:01 -0800974 return input.error("Package" + packageName
975 + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
Winson14ff7172019-10-23 10:42:27 -0700976 }
977
Winsonf00c7552020-01-28 12:52:01 -0800978 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -0700979 }
980
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -0800981 private static ParseResult<ParsingPackage> parseAttribution(ParseInput input,
982 ParsingPackage pkg, Resources res, XmlResourceParser parser)
983 throws IOException, XmlPullParserException {
984 ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res,
985 parser, input);
Winsonf00c7552020-01-28 12:52:01 -0800986 if (result.isError()) {
987 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -0700988 }
Philip P. Moltmann12ac3f42020-03-05 15:01:29 -0800989 return input.success(pkg.addAttribution(result.getResult()));
Winson14ff7172019-10-23 10:42:27 -0700990 }
991
Winsonf00c7552020-01-28 12:52:01 -0800992 private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
993 ParsingPackage pkg, Resources res, XmlResourceParser parser)
Winson14ff7172019-10-23 10:42:27 -0700994 throws XmlPullParserException, IOException {
Winsonf00c7552020-01-28 12:52:01 -0800995 ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
996 pkg, res, parser, PackageParser.sUseRoundIcon, input);
997 if (result.isError()) {
998 return input.error(result);
999 }
1000 return input.success(pkg.addPermissionGroup(result.getResult()));
1001 }
Winson14ff7172019-10-23 10:42:27 -07001002
Winsonf00c7552020-01-28 12:52:01 -08001003 private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
1004 ParsingPackage pkg, Resources res, XmlResourceParser parser)
1005 throws XmlPullParserException, IOException {
1006 ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
1007 pkg, res, parser, PackageParser.sUseRoundIcon, input);
1008 if (result.isError()) {
1009 return input.error(result);
1010 }
1011 return input.success(pkg.addPermission(result.getResult()));
1012 }
Winson14ff7172019-10-23 10:42:27 -07001013
Winsonf00c7552020-01-28 12:52:01 -08001014 private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
1015 ParsingPackage pkg, Resources res, XmlResourceParser parser)
1016 throws XmlPullParserException, IOException {
1017 ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
1018 pkg, res, parser, PackageParser.sUseRoundIcon, input);
1019 if (result.isError()) {
1020 return input.error(result);
1021 }
1022 return input.success(pkg.addPermission(result.getResult()));
1023 }
1024
1025 private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
1026 ParsingPackage pkg, Resources res, XmlResourceParser parser)
1027 throws IOException, XmlPullParserException {
1028 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
1029 try {
1030 // Note: don't allow this value to be a reference to a resource
1031 // that may change.
1032 String name = sa.getNonResourceString(
1033 R.styleable.AndroidManifestUsesPermission_name);
1034
1035 int maxSdkVersion = 0;
1036 TypedValue val = sa.peekValue(
1037 R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
1038 if (val != null) {
1039 if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
1040 maxSdkVersion = val.data;
1041 }
Winson14ff7172019-10-23 10:42:27 -07001042 }
Winson14ff7172019-10-23 10:42:27 -07001043
Winsonf00c7552020-01-28 12:52:01 -08001044 final String requiredFeature = sa.getNonConfigurationString(
1045 R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
Winson14ff7172019-10-23 10:42:27 -07001046
Winsonf00c7552020-01-28 12:52:01 -08001047 final String requiredNotfeature = sa.getNonConfigurationString(
1048 R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
1049 0);
Winson14ff7172019-10-23 10:42:27 -07001050
Winsonf00c7552020-01-28 12:52:01 -08001051 XmlUtils.skipCurrentTag(parser);
Winson14ff7172019-10-23 10:42:27 -07001052
Winsonf00c7552020-01-28 12:52:01 -08001053 // Can only succeed from here on out
1054 ParseResult<ParsingPackage> success = input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -07001055
Winsonf00c7552020-01-28 12:52:01 -08001056 if (name == null) {
1057 return success;
1058 }
Winson14ff7172019-10-23 10:42:27 -07001059
Winsonf00c7552020-01-28 12:52:01 -08001060 if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
1061 return success;
1062 }
1063
1064 // Only allow requesting this permission if the platform supports the given feature.
1065 if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(
1066 requiredFeature)) {
1067 return success;
1068 }
1069
1070 // Only allow requesting this permission if the platform doesn't support the given
1071 // feature.
1072 if (requiredNotfeature != null && mCallback != null
1073 && mCallback.hasFeature(requiredNotfeature)) {
1074 return success;
1075 }
1076
1077 if (!pkg.getRequestedPermissions().contains(name)) {
1078 pkg.addRequestedPermission(name.intern());
1079 } else {
1080 Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
1081 + name + " in package: " + pkg.getPackageName() + " at: "
1082 + parser.getPositionDescription());
1083 }
1084
Winson14ff7172019-10-23 10:42:27 -07001085 return success;
Winsonf00c7552020-01-28 12:52:01 -08001086 } finally {
1087 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07001088 }
Winson14ff7172019-10-23 10:42:27 -07001089 }
1090
Winsonf00c7552020-01-28 12:52:01 -08001091 private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
1092 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
Winson14ff7172019-10-23 10:42:27 -07001093 ConfigurationInfo cPref = new ConfigurationInfo();
Winsonf00c7552020-01-28 12:52:01 -08001094 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
1095 try {
1096 cPref.reqTouchScreen = sa.getInt(
1097 R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
1098 Configuration.TOUCHSCREEN_UNDEFINED);
1099 cPref.reqKeyboardType = sa.getInt(
1100 R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
1101 Configuration.KEYBOARD_UNDEFINED);
1102 if (sa.getBoolean(
1103 R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
1104 false)) {
1105 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
1106 }
1107 cPref.reqNavigation = sa.getInt(
1108 R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
1109 Configuration.NAVIGATION_UNDEFINED);
1110 if (sa.getBoolean(
1111 R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
1112 false)) {
1113 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
1114 }
1115 pkg.addConfigPreference(cPref);
1116 return input.success(pkg);
1117 } finally {
1118 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07001119 }
Winson14ff7172019-10-23 10:42:27 -07001120 }
1121
Winsonf00c7552020-01-28 12:52:01 -08001122 private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
1123 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
Winson14ff7172019-10-23 10:42:27 -07001124 FeatureInfo fi = parseFeatureInfo(res, parser);
Winsonf00c7552020-01-28 12:52:01 -08001125 pkg.addReqFeature(fi);
Winson14ff7172019-10-23 10:42:27 -07001126
1127 if (fi.name == null) {
1128 ConfigurationInfo cPref = new ConfigurationInfo();
1129 cPref.reqGlEsVersion = fi.reqGlEsVersion;
Winsonf00c7552020-01-28 12:52:01 -08001130 pkg.addConfigPreference(cPref);
Winson14ff7172019-10-23 10:42:27 -07001131 }
1132
Winsonf00c7552020-01-28 12:52:01 -08001133 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -07001134 }
1135
1136 private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
1137 FeatureInfo fi = new FeatureInfo();
Winsonf00c7552020-01-28 12:52:01 -08001138 TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
1139 try {
1140 // Note: don't allow this value to be a reference to a resource
1141 // that may change.
1142 fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
1143 fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
1144 if (fi.name == null) {
1145 fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
1146 FeatureInfo.GL_ES_VERSION_UNDEFINED);
1147 }
1148 if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
1149 fi.flags |= FeatureInfo.FLAG_REQUIRED;
1150 }
1151 return fi;
1152 } finally {
1153 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07001154 }
Winson14ff7172019-10-23 10:42:27 -07001155 }
1156
Winsonf00c7552020-01-28 12:52:01 -08001157 private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
1158 ParsingPackage pkg, Resources res, XmlResourceParser parser)
1159 throws IOException, XmlPullParserException {
Winson14ff7172019-10-23 10:42:27 -07001160 FeatureGroupInfo group = new FeatureGroupInfo();
1161 ArrayList<FeatureInfo> features = null;
Winsonf00c7552020-01-28 12:52:01 -08001162 final int depth = parser.getDepth();
Winson14ff7172019-10-23 10:42:27 -07001163 int type;
1164 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1165 && (type != XmlPullParser.END_TAG
Winsonf00c7552020-01-28 12:52:01 -08001166 || parser.getDepth() > depth)) {
1167 if (type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -07001168 continue;
1169 }
1170
1171 final String innerTagName = parser.getName();
1172 if (innerTagName.equals("uses-feature")) {
1173 FeatureInfo featureInfo = parseFeatureInfo(res, parser);
1174 // FeatureGroups are stricter and mandate that
1175 // any <uses-feature> declared are mandatory.
1176 featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
1177 features = ArrayUtils.add(features, featureInfo);
1178 } else {
1179 Slog.w(TAG,
1180 "Unknown element under <feature-group>: " + innerTagName +
Winsonf00c7552020-01-28 12:52:01 -08001181 " at " + pkg.getBaseCodePath() + " " +
Winson14ff7172019-10-23 10:42:27 -07001182 parser.getPositionDescription());
1183 }
Winson14ff7172019-10-23 10:42:27 -07001184 }
1185
1186 if (features != null) {
1187 group.features = new FeatureInfo[features.size()];
1188 group.features = features.toArray(group.features);
1189 }
1190
Winsonf00c7552020-01-28 12:52:01 -08001191 pkg.addFeatureGroup(group);
1192 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -07001193 }
1194
Winsonf00c7552020-01-28 12:52:01 -08001195 private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
1196 ParsingPackage pkg, Resources res, XmlResourceParser parser)
1197 throws IOException, XmlPullParserException {
Winson14ff7172019-10-23 10:42:27 -07001198 if (PackageParser.SDK_VERSION > 0) {
Winsonf00c7552020-01-28 12:52:01 -08001199 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
1200 try {
1201 int minVers = 1;
1202 String minCode = null;
1203 int targetVers = 0;
1204 String targetCode = null;
Winson14ff7172019-10-23 10:42:27 -07001205
Winsonf00c7552020-01-28 12:52:01 -08001206 TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
1207 if (val != null) {
1208 if (val.type == TypedValue.TYPE_STRING && val.string != null) {
1209 minCode = val.string.toString();
1210 } else {
1211 // If it's not a string, it's an integer.
1212 minVers = val.data;
1213 }
Winson14ff7172019-10-23 10:42:27 -07001214 }
Winson14ff7172019-10-23 10:42:27 -07001215
Winsonf00c7552020-01-28 12:52:01 -08001216 val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
1217 if (val != null) {
1218 if (val.type == TypedValue.TYPE_STRING && val.string != null) {
1219 targetCode = val.string.toString();
1220 if (minCode == null) {
1221 minCode = targetCode;
1222 }
1223 } else {
1224 // If it's not a string, it's an integer.
1225 targetVers = val.data;
Winson14ff7172019-10-23 10:42:27 -07001226 }
1227 } else {
Winsonf00c7552020-01-28 12:52:01 -08001228 targetVers = minVers;
1229 targetCode = minCode;
Winson14ff7172019-10-23 10:42:27 -07001230 }
Winson14ff7172019-10-23 10:42:27 -07001231
Winson727da642020-03-10 15:25:32 -07001232 ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
1233 targetVers, targetCode, PackageParser.SDK_CODENAMES, input);
1234 if (targetSdkVersionResult.isError()) {
1235 return input.error(targetSdkVersionResult);
1236 }
1237
1238 int targetSdkVersion = targetSdkVersionResult.getResult();
1239
1240 ParseResult<?> deferResult =
1241 input.enableDeferredError(pkg.getPackageName(), targetSdkVersion);
1242 if (deferResult.isError()) {
1243 return input.error(deferResult);
1244 }
1245
Winsonf00c7552020-01-28 12:52:01 -08001246 ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
1247 PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input);
1248 if (minSdkVersionResult.isError()) {
1249 return input.error(minSdkVersionResult);
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001250 }
Winsonf00c7552020-01-28 12:52:01 -08001251
1252 int minSdkVersion = minSdkVersionResult.getResult();
1253
Winsonf00c7552020-01-28 12:52:01 -08001254 pkg.setMinSdkVersion(minSdkVersion)
1255 .setTargetSdkVersion(targetSdkVersion);
1256
1257 int type;
1258 final int innerDepth = parser.getDepth();
Oli Lan72ae4472020-04-14 16:50:41 +01001259 SparseIntArray minExtensionVersions = null;
Winsonf00c7552020-01-28 12:52:01 -08001260 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1261 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1262 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1263 continue;
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001264 }
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001265
Winsonf00c7552020-01-28 12:52:01 -08001266 final ParseResult result;
1267 if (parser.getName().equals("extension-sdk")) {
Oli Lan72ae4472020-04-14 16:50:41 +01001268 if (minExtensionVersions == null) {
1269 minExtensionVersions = new SparseIntArray();
1270 }
1271 result = parseExtensionSdk(input, res, parser, minExtensionVersions);
Winsonf00c7552020-01-28 12:52:01 -08001272 XmlUtils.skipCurrentTag(parser);
1273 } else {
1274 result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
1275 }
1276
1277 if (result.isError()) {
1278 return input.error(result);
1279 }
1280 }
Oli Lan72ae4472020-04-14 16:50:41 +01001281 pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions));
Winsonf00c7552020-01-28 12:52:01 -08001282 } finally {
1283 sa.recycle();
1284 }
Winson14ff7172019-10-23 10:42:27 -07001285 }
Winsonf00c7552020-01-28 12:52:01 -08001286 return input.success(pkg);
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001287 }
Winson14ff7172019-10-23 10:42:27 -07001288
Oli Lan72ae4472020-04-14 16:50:41 +01001289 @Nullable
1290 private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) {
1291 if (input == null) {
1292 return null;
1293 }
1294 SparseIntArray output = new SparseIntArray(input.size());
1295 for (int i = 0; i < input.size(); i++) {
1296 output.put(input.keyAt(i), input.valueAt(i));
1297 }
1298 return output;
1299 }
1300
1301 private static ParseResult<SparseIntArray> parseExtensionSdk(
1302 ParseInput input, Resources res, XmlResourceParser parser,
1303 SparseIntArray minExtensionVersions) {
Winsonf00c7552020-01-28 12:52:01 -08001304 int sdkVersion;
1305 int minVersion;
1306 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
1307 try {
1308 sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
1309 minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
1310 } finally {
1311 sa.recycle();
1312 }
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001313
1314 if (sdkVersion < 0) {
Winsonf00c7552020-01-28 12:52:01 -08001315 return input.error(
1316 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1317 "<extension-sdk> must specify an sdkVersion >= 0");
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001318 }
1319 if (minVersion < 0) {
Winsonf00c7552020-01-28 12:52:01 -08001320 return input.error(
1321 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1322 "<extension-sdk> must specify minExtensionVersion >= 0");
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001323 }
1324
1325 try {
1326 int version = SdkExtensions.getExtensionVersion(sdkVersion);
1327 if (version < minVersion) {
Winsonf00c7552020-01-28 12:52:01 -08001328 return input.error(
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001329 PackageManager.INSTALL_FAILED_OLDER_SDK,
1330 "Package requires " + sdkVersion + " extension version " + minVersion
1331 + " which exceeds device version " + version);
1332 }
1333 } catch (RuntimeException e) {
Winsonf00c7552020-01-28 12:52:01 -08001334 return input.error(
Anton Hanssonb2f709d2020-01-09 10:25:23 +00001335 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1336 "Specified sdkVersion " + sdkVersion + " is not valid");
1337 }
Oli Lan72ae4472020-04-14 16:50:41 +01001338 minExtensionVersions.put(sdkVersion, minVersion);
1339 return input.success(minExtensionVersions);
Winson14ff7172019-10-23 10:42:27 -07001340 }
1341
Winsonf00c7552020-01-28 12:52:01 -08001342 /**
1343 * {@link ParseResult} version of
1344 * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])}
1345 */
1346 public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
1347 @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
1348 @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) {
1349 // If it's a release SDK, make sure we meet the minimum SDK requirement.
1350 if (minCode == null) {
1351 if (minVers <= platformSdkVersion) {
1352 return input.success(minVers);
Winson14ff7172019-10-23 10:42:27 -07001353 }
Winsonf00c7552020-01-28 12:52:01 -08001354
1355 // We don't meet the minimum SDK requirement.
1356 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1357 "Requires newer sdk version #" + minVers
1358 + " (current version is #" + platformSdkVersion + ")");
Winson14ff7172019-10-23 10:42:27 -07001359 }
1360
Winsonf00c7552020-01-28 12:52:01 -08001361 // If it's a pre-release SDK and the codename matches this platform, we
1362 // definitely meet the minimum SDK requirement.
1363 if (matchTargetCode(platformSdkCodenames, minCode)) {
1364 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
1365 }
1366
1367 // Otherwise, we're looking at an incompatible pre-release SDK.
1368 if (platformSdkCodenames.length > 0) {
1369 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1370 "Requires development platform " + minCode
1371 + " (current platform is any of "
1372 + Arrays.toString(platformSdkCodenames) + ")");
1373 } else {
1374 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1375 "Requires development platform " + minCode
1376 + " but this is a release platform.");
1377 }
Winson14ff7172019-10-23 10:42:27 -07001378 }
1379
Winsonf00c7552020-01-28 12:52:01 -08001380 /**
1381 * {@link ParseResult} version of
1382 * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])}
1383 */
1384 public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
1385 @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
1386 @NonNull ParseInput input) {
1387 // If it's a release SDK, return the version number unmodified.
1388 if (targetCode == null) {
1389 return input.success(targetVers);
1390 }
Winson14ff7172019-10-23 10:42:27 -07001391
Winsonf00c7552020-01-28 12:52:01 -08001392 // If it's a pre-release SDK and the codename matches this platform, it
1393 // definitely targets this SDK.
1394 if (matchTargetCode(platformSdkCodenames, targetCode)) {
1395 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
1396 }
1397
1398 // Otherwise, we're looking at an incompatible pre-release SDK.
1399 if (platformSdkCodenames.length > 0) {
1400 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1401 "Requires development platform " + targetCode
1402 + " (current platform is any of "
1403 + Arrays.toString(platformSdkCodenames) + ")");
1404 } else {
1405 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
1406 "Requires development platform " + targetCode
1407 + " but this is a release platform.");
1408 }
1409 }
1410
1411 /**
1412 * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
1413 * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
1414 * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
1415 */
1416 private static boolean matchTargetCode(@NonNull String[] codeNames,
1417 @NonNull String targetCode) {
1418 final String targetCodeName;
1419 final int targetCodeIdx = targetCode.indexOf('.');
1420 if (targetCodeIdx == -1) {
1421 targetCodeName = targetCode;
1422 } else {
1423 targetCodeName = targetCode.substring(0, targetCodeIdx);
1424 }
1425 return ArrayUtils.contains(codeNames, targetCodeName);
1426 }
1427
1428 private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
1429 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
1430 if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
1431 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
1432 try {
1433 final String hash = sa.getNonConfigurationString(
1434 R.styleable.AndroidManifestRestrictUpdate_hash,
1435 0);
1436
1437 if (hash != null) {
1438 final int hashLength = hash.length();
1439 final byte[] hashBytes = new byte[hashLength / 2];
1440 for (int i = 0; i < hashLength; i += 2) {
1441 hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
1442 << 4)
1443 + Character.digit(hash.charAt(i + 1), 16));
1444 }
1445 pkg.setRestrictUpdateHash(hashBytes);
1446 } else {
1447 pkg.setRestrictUpdateHash(null);
1448 }
1449 } finally {
1450 sa.recycle();
1451 }
1452 }
1453 return input.success(pkg);
1454 }
1455
1456 private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
1457 Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
1458 final int depth = parser.getDepth();
Winson14ff7172019-10-23 10:42:27 -07001459 int type;
1460 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
1461 && (type != XmlPullParser.END_TAG
Winsonf00c7552020-01-28 12:52:01 -08001462 || parser.getDepth() > depth)) {
1463 if (type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -07001464 continue;
1465 }
1466 if (parser.getName().equals("intent")) {
Winsonf00c7552020-01-28 12:52:01 -08001467 ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null,
1468 pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input);
1469 if (result.isError()) {
1470 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -07001471 }
1472
Winsonf00c7552020-01-28 12:52:01 -08001473 ParsedIntentInfo intentInfo = result.getResult();
1474
Winson14ff7172019-10-23 10:42:27 -07001475 Uri data = null;
1476 String dataType = null;
Patrick Baumann28a68102020-03-17 10:40:49 -07001477 String host = IntentFilter.WILDCARD;
Winson14ff7172019-10-23 10:42:27 -07001478 final int numActions = intentInfo.countActions();
1479 final int numSchemes = intentInfo.countDataSchemes();
1480 final int numTypes = intentInfo.countDataTypes();
1481 final int numHosts = intentInfo.getHosts().length;
1482 if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
Winsonf00c7552020-01-28 12:52:01 -08001483 return input.error("intent tags must contain either an action or data.");
Winson14ff7172019-10-23 10:42:27 -07001484 }
1485 if (numActions > 1) {
Winsonf00c7552020-01-28 12:52:01 -08001486 return input.error("intent tag may have at most one action.");
Winson14ff7172019-10-23 10:42:27 -07001487 }
1488 if (numTypes > 1) {
Winsonf00c7552020-01-28 12:52:01 -08001489 return input.error("intent tag may have at most one data type.");
Winson14ff7172019-10-23 10:42:27 -07001490 }
1491 if (numSchemes > 1) {
Winsonf00c7552020-01-28 12:52:01 -08001492 return input.error("intent tag may have at most one data scheme.");
Winson14ff7172019-10-23 10:42:27 -07001493 }
1494 if (numHosts > 1) {
Winsonf00c7552020-01-28 12:52:01 -08001495 return input.error("intent tag may have at most one data host.");
Winson14ff7172019-10-23 10:42:27 -07001496 }
1497 Intent intent = new Intent();
1498 for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
1499 intent.addCategory(intentInfo.getCategory(i));
1500 }
1501 if (numHosts == 1) {
1502 host = intentInfo.getHosts()[0];
1503 }
1504 if (numSchemes == 1) {
1505 data = new Uri.Builder()
1506 .scheme(intentInfo.getDataScheme(0))
1507 .authority(host)
Patrick Baumann28a68102020-03-17 10:40:49 -07001508 .path(IntentFilter.WILDCARD_PATH)
Winson14ff7172019-10-23 10:42:27 -07001509 .build();
1510 }
1511 if (numTypes == 1) {
1512 dataType = intentInfo.getDataType(0);
Patrick Baumann28a68102020-03-17 10:40:49 -07001513 // The dataType may have had the '/' removed for the dynamic mimeType feature.
1514 // If we detect that case, we add the * back.
1515 if (!dataType.contains("/")) {
1516 dataType = dataType + "/*";
1517 }
1518 if (data == null) {
1519 data = new Uri.Builder()
1520 .scheme("content")
1521 .authority(IntentFilter.WILDCARD)
1522 .path(IntentFilter.WILDCARD_PATH)
1523 .build();
1524 }
Winson14ff7172019-10-23 10:42:27 -07001525 }
1526 intent.setDataAndType(data, dataType);
1527 if (numActions == 1) {
1528 intent.setAction(intentInfo.getAction(0));
1529 }
Winsonf00c7552020-01-28 12:52:01 -08001530 pkg.addQueriesIntent(intent);
Winson14ff7172019-10-23 10:42:27 -07001531 } else if (parser.getName().equals("package")) {
1532 final TypedArray sa = res.obtainAttributes(parser,
1533 R.styleable.AndroidManifestQueriesPackage);
Winsonf00c7552020-01-28 12:52:01 -08001534 final String packageName = sa.getString(
1535 R.styleable.AndroidManifestQueriesPackage_name);
Winson14ff7172019-10-23 10:42:27 -07001536 if (TextUtils.isEmpty(packageName)) {
Winsonf00c7552020-01-28 12:52:01 -08001537 return input.error("Package name is missing from package tag.");
Winson14ff7172019-10-23 10:42:27 -07001538 }
Winsonf00c7552020-01-28 12:52:01 -08001539 pkg.addQueriesPackage(packageName.intern());
Patrick Baumann99181232020-01-28 10:55:25 -08001540 } else if (parser.getName().equals("provider")) {
1541 final TypedArray sa = res.obtainAttributes(parser,
1542 R.styleable.AndroidManifestQueriesProvider);
1543 try {
1544 final String authorities =
1545 sa.getString(R.styleable.AndroidManifestQueriesProvider_authorities);
1546 if (TextUtils.isEmpty(authorities)) {
Winsonf00c7552020-01-28 12:52:01 -08001547 return input.error(
Patrick Baumann99181232020-01-28 10:55:25 -08001548 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
1549 "Authority missing from provider tag."
1550 );
1551 }
1552 StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
1553 while (authoritiesTokenizer.hasMoreElements()) {
Winsonf00c7552020-01-28 12:52:01 -08001554 pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
Patrick Baumann99181232020-01-28 10:55:25 -08001555 }
1556 } finally {
1557 sa.recycle();
1558 }
Winson14ff7172019-10-23 10:42:27 -07001559 }
1560 }
Winsonf00c7552020-01-28 12:52:01 -08001561 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -07001562 }
1563
1564 /**
1565 * Parse the {@code application} XML tree at the current parse location in a
1566 * <em>base APK</em> manifest.
1567 * <p>
1568 * When adding new features, carefully consider if they should also be
1569 * supported by split APKs.
1570 *
Winsonf00c7552020-01-28 12:52:01 -08001571 * This method should avoid using a getter for fields set by this method. Prefer assigning
1572 * a local variable and using it. Otherwise there's an ordering problem which can be broken
1573 * if any code moves around.
Winson14ff7172019-10-23 10:42:27 -07001574 */
Winsonf00c7552020-01-28 12:52:01 -08001575 private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
1576 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
1577 throws XmlPullParserException, IOException {
1578 final String pkgName = pkg.getPackageName();
1579 int targetSdk = pkg.getTargetSdkVersion();
Winson14ff7172019-10-23 10:42:27 -07001580
Winsonf00c7552020-01-28 12:52:01 -08001581 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
Winson14ff7172019-10-23 10:42:27 -07001582 try {
Winsonf00c7552020-01-28 12:52:01 -08001583 // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
1584 // This case can only happen in unit tests where we sometimes need to create fakes
1585 // of various package parser data structures.
1586 if (sa == null) {
1587 return input.error("<application> does not contain any attributes");
Winson14ff7172019-10-23 10:42:27 -07001588 }
1589
Winsonf00c7552020-01-28 12:52:01 -08001590 String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
1591 0);
1592 if (name != null) {
1593 String packageName = pkg.getPackageName();
1594 String outInfoName = ParsingUtils.buildClassName(packageName, name);
1595 if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
1596 return input.error("<application> invalid android:name");
1597 } else if (outInfoName == null) {
1598 return input.error("Empty class name in package " + packageName);
Winson14ff7172019-10-23 10:42:27 -07001599 }
1600
Winsonf00c7552020-01-28 12:52:01 -08001601 pkg.setClassName(outInfoName);
Winson14ff7172019-10-23 10:42:27 -07001602 }
1603
Winsonf00c7552020-01-28 12:52:01 -08001604 TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
1605 if (labelValue != null) {
1606 pkg.setLabelRes(labelValue.resourceId);
1607 if (labelValue.resourceId == 0) {
1608 pkg.setNonLocalizedLabel(labelValue.coerceToString());
1609 }
1610 }
Winson14ff7172019-10-23 10:42:27 -07001611
Winsonf00c7552020-01-28 12:52:01 -08001612 parseBaseAppBasicFlags(pkg, sa);
1613
1614 String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
1615 R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
1616 if (manageSpaceActivity != null) {
1617 String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
1618 manageSpaceActivity);
1619
1620 if (manageSpaceActivityName == null) {
1621 return input.error("Empty class name in package " + pkgName);
1622 }
1623
1624 pkg.setManageSpaceActivityName(manageSpaceActivityName);
1625 }
1626
1627 if (pkg.isAllowBackup()) {
Winson14ff7172019-10-23 10:42:27 -07001628 // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
1629 // and restoreAnyVersion are only relevant if backup is possible for the
1630 // given application.
Winsonf00c7552020-01-28 12:52:01 -08001631 String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
1632 R.styleable.AndroidManifestApplication_backupAgent, sa);
Winson14ff7172019-10-23 10:42:27 -07001633 if (backupAgent != null) {
Winsonf00c7552020-01-28 12:52:01 -08001634 String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
Winson14ff7172019-10-23 10:42:27 -07001635 if (backupAgentName == null) {
Winsonf00c7552020-01-28 12:52:01 -08001636 return input.error("Empty class name in package " + pkgName);
Winson14ff7172019-10-23 10:42:27 -07001637 }
1638
1639 if (PackageParser.DEBUG_BACKUP) {
1640 Slog.v(TAG, "android:backupAgent = " + backupAgentName
1641 + " from " + pkgName + "+" + backupAgent);
1642 }
1643
Winsonf00c7552020-01-28 12:52:01 -08001644 pkg.setBackupAgentName(backupAgentName)
1645 .setKillAfterRestore(bool(true,
1646 R.styleable.AndroidManifestApplication_killAfterRestore, sa))
1647 .setRestoreAnyVersion(bool(false,
1648 R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
1649 .setFullBackupOnly(bool(false,
1650 R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
1651 .setBackupInForeground(bool(false,
1652 R.styleable.AndroidManifestApplication_backupInForeground, sa));
Winson14ff7172019-10-23 10:42:27 -07001653 }
1654
1655 TypedValue v = sa.peekValue(
1656 R.styleable.AndroidManifestApplication_fullBackupContent);
1657 int fullBackupContent = 0;
1658
1659 if (v != null) {
1660 fullBackupContent = v.resourceId;
1661
1662 if (v.resourceId == 0) {
1663 if (PackageParser.DEBUG_BACKUP) {
1664 Slog.v(TAG, "fullBackupContent specified as boolean=" +
1665 (v.data == 0 ? "false" : "true"));
1666 }
1667 // "false" => -1, "true" => 0
1668 fullBackupContent = v.data == 0 ? -1 : 0;
1669 }
1670
Winsonf00c7552020-01-28 12:52:01 -08001671 pkg.setFullBackupContent(fullBackupContent);
Winson14ff7172019-10-23 10:42:27 -07001672 }
1673 if (PackageParser.DEBUG_BACKUP) {
1674 Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
1675 }
1676 }
1677
Winsonf00c7552020-01-28 12:52:01 -08001678 if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
Winson14ff7172019-10-23 10:42:27 -07001679 // Check if persistence is based on a feature being present
1680 final String requiredFeature = sa.getNonResourceString(R.styleable
1681 .AndroidManifestApplication_persistentWhenFeatureAvailable);
Winsonf00c7552020-01-28 12:52:01 -08001682 pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
Winson14ff7172019-10-23 10:42:27 -07001683 }
1684
Winsonf00c7552020-01-28 12:52:01 -08001685 // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a
1686 // util or just have PackageImpl return true if either flag is set
1687 // Debuggable implies profileable
1688 pkg.setProfileableByShell(pkg.isProfileableByShell() || pkg.isDebuggable());
Winson14ff7172019-10-23 10:42:27 -07001689
1690 if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
Winsonf00c7552020-01-28 12:52:01 -08001691 pkg.setResizeableActivity(sa.getBoolean(
Winson14ff7172019-10-23 10:42:27 -07001692 R.styleable.AndroidManifestApplication_resizeableActivity, true));
1693 } else {
Winsonf00c7552020-01-28 12:52:01 -08001694 pkg.setResizeableActivityViaSdkVersion(
1695 targetSdk >= Build.VERSION_CODES.N);
Winson14ff7172019-10-23 10:42:27 -07001696 }
1697
Winsonf00c7552020-01-28 12:52:01 -08001698 String taskAffinity;
1699 if (targetSdk >= Build.VERSION_CODES.FROYO) {
1700 taskAffinity = sa.getNonConfigurationString(
Winson14ff7172019-10-23 10:42:27 -07001701 R.styleable.AndroidManifestApplication_taskAffinity,
1702 Configuration.NATIVE_CONFIG_VERSION);
1703 } else {
1704 // Some older apps have been seen to use a resource reference
1705 // here that on older builds was ignored (with a warning). We
1706 // need to continue to do this for them so they don't break.
Winsonf00c7552020-01-28 12:52:01 -08001707 taskAffinity = sa.getNonResourceString(
Winson14ff7172019-10-23 10:42:27 -07001708 R.styleable.AndroidManifestApplication_taskAffinity);
1709 }
Winson14ff7172019-10-23 10:42:27 -07001710
Winsonf00c7552020-01-28 12:52:01 -08001711 ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
1712 pkgName, pkgName, taskAffinity, input);
1713 if (taskAffinityResult.isError()) {
1714 return input.error(taskAffinityResult);
Winson14ff7172019-10-23 10:42:27 -07001715 }
1716
Winsonf00c7552020-01-28 12:52:01 -08001717 pkg.setTaskAffinity(taskAffinityResult.getResult());
Winson14ff7172019-10-23 10:42:27 -07001718 String factory = sa.getNonResourceString(
1719 R.styleable.AndroidManifestApplication_appComponentFactory);
1720 if (factory != null) {
Winsonf00c7552020-01-28 12:52:01 -08001721 String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
Winson14ff7172019-10-23 10:42:27 -07001722 if (appComponentFactory == null) {
Winsonf00c7552020-01-28 12:52:01 -08001723 return input.error("Empty class name in package " + pkgName);
Winson14ff7172019-10-23 10:42:27 -07001724 }
1725
Winsonf00c7552020-01-28 12:52:01 -08001726 pkg.setAppComponentFactory(appComponentFactory);
Winson14ff7172019-10-23 10:42:27 -07001727 }
1728
Winson14ff7172019-10-23 10:42:27 -07001729 CharSequence pname;
Winsonf00c7552020-01-28 12:52:01 -08001730 if (targetSdk >= Build.VERSION_CODES.FROYO) {
Winson14ff7172019-10-23 10:42:27 -07001731 pname = sa.getNonConfigurationString(
1732 R.styleable.AndroidManifestApplication_process,
1733 Configuration.NATIVE_CONFIG_VERSION);
1734 } else {
1735 // Some older apps have been seen to use a resource reference
1736 // here that on older builds was ignored (with a warning). We
1737 // need to continue to do this for them so they don't break.
1738 pname = sa.getNonResourceString(
1739 R.styleable.AndroidManifestApplication_process);
1740 }
Winsonf00c7552020-01-28 12:52:01 -08001741 ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
1742 pkgName, null, pname, flags, mSeparateProcesses, input);
1743 if (processNameResult.isError()) {
1744 return input.error(processNameResult);
Winson14ff7172019-10-23 10:42:27 -07001745 }
1746
Winsonf00c7552020-01-28 12:52:01 -08001747 String processName = processNameResult.getResult();
1748 pkg.setProcessName(processName);
Winson14ff7172019-10-23 10:42:27 -07001749
Winsonf00c7552020-01-28 12:52:01 -08001750 if (pkg.isCantSaveState()) {
Winson14ff7172019-10-23 10:42:27 -07001751 // A heavy-weight application can not be in a custom process.
1752 // We can do direct compare because we intern all strings.
Winsonf00c7552020-01-28 12:52:01 -08001753 if (processName != null && !processName.equals(pkgName)) {
1754 return input.error(
1755 "cantSaveState applications can not use custom processes");
Winson14ff7172019-10-23 10:42:27 -07001756 }
1757 }
1758
Winsonf00c7552020-01-28 12:52:01 -08001759 String classLoaderName = pkg.getClassLoaderName();
Winson14ff7172019-10-23 10:42:27 -07001760 if (classLoaderName != null
1761 && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
Winsonf00c7552020-01-28 12:52:01 -08001762 return input.error("Invalid class loader name: " + classLoaderName);
Winson14ff7172019-10-23 10:42:27 -07001763 }
Evgenii Stepanov102d3d82020-02-12 16:48:14 -08001764
Evgenii Stepanovd43d1092020-03-16 13:55:42 -07001765 pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
Winson14ff7172019-10-23 10:42:27 -07001766 } finally {
Winsonf00c7552020-01-28 12:52:01 -08001767 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07001768 }
1769
Winson14ff7172019-10-23 10:42:27 -07001770 boolean hasActivityOrder = false;
1771 boolean hasReceiverOrder = false;
1772 boolean hasServiceOrder = false;
Winsonf00c7552020-01-28 12:52:01 -08001773 final int depth = parser.getDepth();
1774 int type;
Winson14ff7172019-10-23 10:42:27 -07001775 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Winsonf00c7552020-01-28 12:52:01 -08001776 && (type != XmlPullParser.END_TAG
1777 || parser.getDepth() > depth)) {
1778 if (type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -07001779 continue;
1780 }
1781
Winsonf00c7552020-01-28 12:52:01 -08001782 final ParseResult result;
Winson14ff7172019-10-23 10:42:27 -07001783 String tagName = parser.getName();
Winsonf00c7552020-01-28 12:52:01 -08001784 boolean isActivity = false;
Winson14ff7172019-10-23 10:42:27 -07001785 switch (tagName) {
1786 case "activity":
Winsonf00c7552020-01-28 12:52:01 -08001787 isActivity = true;
1788 // fall-through
Winson14ff7172019-10-23 10:42:27 -07001789 case "receiver":
Winsonf00c7552020-01-28 12:52:01 -08001790 ParseResult<ParsedActivity> activityResult =
1791 ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
1792 res, parser, flags, PackageParser.sUseRoundIcon, input);
1793
1794 if (activityResult.isSuccess()) {
1795 ParsedActivity activity = activityResult.getResult();
1796 if (isActivity) {
1797 hasActivityOrder |= (activity.getOrder() != 0);
1798 pkg.addActivity(activity);
1799 } else {
1800 hasReceiverOrder |= (activity.getOrder() != 0);
1801 pkg.addReceiver(activity);
1802 }
Winson14ff7172019-10-23 10:42:27 -07001803 }
1804
Winsonf00c7552020-01-28 12:52:01 -08001805 result = activityResult;
Winson14ff7172019-10-23 10:42:27 -07001806 break;
1807 case "service":
Winsonf00c7552020-01-28 12:52:01 -08001808 ParseResult<ParsedService> serviceResult =
1809 ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
1810 flags, PackageParser.sUseRoundIcon, input);
1811 if (serviceResult.isSuccess()) {
1812 ParsedService service = serviceResult.getResult();
1813 hasServiceOrder |= (service.getOrder() != 0);
1814 pkg.addService(service);
Winson14ff7172019-10-23 10:42:27 -07001815 }
1816
Winsonf00c7552020-01-28 12:52:01 -08001817 result = serviceResult;
Winson14ff7172019-10-23 10:42:27 -07001818 break;
1819 case "provider":
Winsonf00c7552020-01-28 12:52:01 -08001820 ParseResult<ParsedProvider> providerResult =
1821 ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
1822 flags, PackageParser.sUseRoundIcon, input);
1823 if (providerResult.isSuccess()) {
1824 pkg.addProvider(providerResult.getResult());
Winson14ff7172019-10-23 10:42:27 -07001825 }
1826
Winsonf00c7552020-01-28 12:52:01 -08001827 result = providerResult;
Winson14ff7172019-10-23 10:42:27 -07001828 break;
1829 case "activity-alias":
Winsonf00c7552020-01-28 12:52:01 -08001830 activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
1831 parser, PackageParser.sUseRoundIcon, input);
1832 if (activityResult.isSuccess()) {
1833 ParsedActivity activity = activityResult.getResult();
1834 hasActivityOrder |= (activity.getOrder() != 0);
1835 pkg.addActivity(activity);
Winson14ff7172019-10-23 10:42:27 -07001836 }
1837
Winsonf00c7552020-01-28 12:52:01 -08001838 result = activityResult;
Winson7ea528a2019-12-13 14:41:40 -08001839 break;
Winson14ff7172019-10-23 10:42:27 -07001840 default:
Winsonf00c7552020-01-28 12:52:01 -08001841 result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
1842 break;
1843 }
1844
1845 if (result.isError()) {
1846 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -07001847 }
1848 }
1849
Winsonf00c7552020-01-28 12:52:01 -08001850 if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
Winson14ff7172019-10-23 10:42:27 -07001851 // Add a hidden app detail activity to normal apps which forwards user to App Details
1852 // page.
Winsonf00c7552020-01-28 12:52:01 -08001853 ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
Winson727da642020-03-10 15:25:32 -07001854 if (a.isError()) {
1855 // Error should be impossible here, as the only failure case as of SDK R is a
1856 // string validation error on a constant ":app_details" string passed in by the
1857 // parsing code itself. For this reason, this is just a hard failure instead of
1858 // deferred.
1859 return input.error(a);
1860 }
1861
Winsonf00c7552020-01-28 12:52:01 -08001862 pkg.addActivity(a.getResult());
Winson14ff7172019-10-23 10:42:27 -07001863 }
1864
1865 if (hasActivityOrder) {
Winsonf00c7552020-01-28 12:52:01 -08001866 pkg.sortActivities();
Winson14ff7172019-10-23 10:42:27 -07001867 }
1868 if (hasReceiverOrder) {
Winsonf00c7552020-01-28 12:52:01 -08001869 pkg.sortReceivers();
Winson14ff7172019-10-23 10:42:27 -07001870 }
1871 if (hasServiceOrder) {
Winsonf00c7552020-01-28 12:52:01 -08001872 pkg.sortServices();
Winson14ff7172019-10-23 10:42:27 -07001873 }
Winsonf00c7552020-01-28 12:52:01 -08001874
1875 // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
Winson14ff7172019-10-23 10:42:27 -07001876 // every activity info has had a chance to set it from its attributes.
Winsonf00c7552020-01-28 12:52:01 -08001877 setMaxAspectRatio(pkg);
1878 setMinAspectRatio(pkg);
Winson14ff7172019-10-23 10:42:27 -07001879
Winsonf00c7552020-01-28 12:52:01 -08001880 pkg.setHasDomainUrls(hasDomainURLs(pkg));
Winson14ff7172019-10-23 10:42:27 -07001881
Winsonf00c7552020-01-28 12:52:01 -08001882 return input.success(pkg);
Winson14ff7172019-10-23 10:42:27 -07001883 }
1884
Winsonf00c7552020-01-28 12:52:01 -08001885 /**
1886 * Collection of single-line, no (or little) logic assignments. Separated for readability.
1887 *
1888 * Flags are separated by type and by default value. They are sorted alphabetically within each
1889 * section.
1890 */
1891 private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
1892 int targetSdk = pkg.getTargetSdkVersion();
1893 //@formatter:off
1894 // CHECKSTYLE:off
1895 pkg
1896 // Default true
1897 .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
1898 .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
1899 .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
1900 .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
1901 .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
1902 .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
1903 .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
1904 // Default false
1905 .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
1906 .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
Winsona20ba0b2020-03-03 13:17:36 -08001907 .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa))
Winsonf00c7552020-01-28 12:52:01 -08001908 .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
1909 .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
1910 .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
1911 .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
1912 .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
1913 .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
1914 .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
1915 .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
1916 .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
1917 .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
1918 .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
1919 .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
1920 .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
1921 .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
1922 .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
Eugene Susla49b84c32020-03-23 15:19:29 -07001923 .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
Winsonf00c7552020-01-28 12:52:01 -08001924 // targetSdkVersion gated
1925 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
1926 .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
1927 .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
1928 .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
1929 // Ints Default 0
1930 .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
1931 // Ints
1932 .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
1933 // Floats Default 0f
1934 .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
1935 .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
1936 // Resource ID
1937 .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
1938 .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
1939 .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))
1940 .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
1941 .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
1942 .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
1943 .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
1944 // Strings
1945 .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
1946 .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
1947 .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
1948 .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
1949 // Non-Config String
1950 .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
1951 // CHECKSTYLE:on
1952 //@formatter:on
1953 }
Winson14ff7172019-10-23 10:42:27 -07001954
Winsonf00c7552020-01-28 12:52:01 -08001955 /**
1956 * For parsing non-MainComponents. Main ones have an order and some special handling which is
1957 * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
1958 * XmlResourceParser, int)}.
1959 */
1960 private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
1961 Resources res, XmlResourceParser parser, int flags)
1962 throws IOException, XmlPullParserException {
1963 switch (tag) {
1964 case "meta-data":
1965 // TODO(b/135203078): I have no idea what this comment means
1966 // note: application meta-data is stored off to the side, so it can
1967 // remain null in the primary copy (we like to avoid extra copies because
1968 // it can be large)
1969 ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
1970 pkg.getMetaData(), input);
1971 if (metaDataResult.isSuccess()) {
1972 pkg.setMetaData(metaDataResult.getResult());
1973 }
Winson14ff7172019-10-23 10:42:27 -07001974
Winsonf00c7552020-01-28 12:52:01 -08001975 return metaDataResult;
1976 case "static-library":
1977 return parseStaticLibrary(pkg, res, parser, input);
1978 case "library":
1979 return parseLibrary(pkg, res, parser, input);
1980 case "uses-static-library":
1981 return parseUsesStaticLibrary(input, pkg, res, parser);
1982 case "uses-library":
1983 return parseUsesLibrary(input, pkg, res, parser);
1984 case "processes":
1985 return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
1986 case "uses-package":
1987 // Dependencies for app installers; we don't currently try to
1988 // enforce this.
1989 return input.success(null);
1990 case "profileable":
1991 return parseProfileable(input, pkg, res, parser);
1992 default:
1993 return ParsingUtils.unknownTag("<application>", pkg, parser, input);
Winson14ff7172019-10-23 10:42:27 -07001994 }
Winsonf00c7552020-01-28 12:52:01 -08001995 }
Winson14ff7172019-10-23 10:42:27 -07001996
Winsonf00c7552020-01-28 12:52:01 -08001997 @NonNull
1998 private static ParseResult<ParsingPackage> parseStaticLibrary(
1999 ParsingPackage pkg, Resources res,
2000 XmlResourceParser parser, ParseInput input) {
2001 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
2002 try {
2003 // Note: don't allow this value to be a reference to a resource
2004 // that may change.
2005 String lname = sa.getNonResourceString(
2006 R.styleable.AndroidManifestStaticLibrary_name);
2007 final int version = sa.getInt(
2008 R.styleable.AndroidManifestStaticLibrary_version, -1);
2009 final int versionMajor = sa.getInt(
2010 R.styleable.AndroidManifestStaticLibrary_versionMajor,
2011 0);
Winson14ff7172019-10-23 10:42:27 -07002012
Winsonf00c7552020-01-28 12:52:01 -08002013 // Since the app canot run without a static lib - fail if malformed
2014 if (lname == null || version < 0) {
2015 return input.error("Bad static-library declaration name: " + lname
2016 + " version: " + version);
2017 } else if (pkg.getSharedUserId() != null) {
2018 return input.error(
2019 PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
2020 "sharedUserId not allowed in static shared library"
Winson14ff7172019-10-23 10:42:27 -07002021 );
Winsonf00c7552020-01-28 12:52:01 -08002022 } else if (pkg.getStaticSharedLibName() != null) {
2023 return input.error("Multiple static-shared libs for package "
2024 + pkg.getPackageName());
Winson14ff7172019-10-23 10:42:27 -07002025 }
Winsonf00c7552020-01-28 12:52:01 -08002026
2027 return input.success(pkg.setStaticSharedLibName(lname.intern())
2028 .setStaticSharedLibVersion(
2029 PackageInfo.composeLongVersionCode(versionMajor, version))
2030 .setStaticSharedLibrary(true));
2031 } finally {
2032 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07002033 }
Winson14ff7172019-10-23 10:42:27 -07002034 }
2035
Winsonf00c7552020-01-28 12:52:01 -08002036 @NonNull
2037 private static ParseResult<ParsingPackage> parseLibrary(
2038 ParsingPackage pkg, Resources res,
2039 XmlResourceParser parser, ParseInput input) {
2040 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
2041 try {
2042 // Note: don't allow this value to be a reference to a resource
2043 // that may change.
2044 String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
Winson14ff7172019-10-23 10:42:27 -07002045
Winsonf00c7552020-01-28 12:52:01 -08002046 if (lname != null) {
2047 lname = lname.intern();
2048 if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
2049 pkg.addLibraryName(lname);
2050 }
2051 }
2052 return input.success(pkg);
2053 } finally {
2054 sa.recycle();
2055 }
2056 }
2057
2058 @NonNull
2059 private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
2060 ParsingPackage pkg, Resources res, XmlResourceParser parser)
2061 throws XmlPullParserException, IOException {
2062 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
2063 try {
2064 // Note: don't allow this value to be a reference to a resource that may change.
2065 String lname = sa.getNonResourceString(
2066 R.styleable.AndroidManifestUsesLibrary_name);
2067 final int version = sa.getInt(
2068 R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
2069 String certSha256Digest = sa.getNonResourceString(R.styleable
2070 .AndroidManifestUsesStaticLibrary_certDigest);
2071
2072 // Since an APK providing a static shared lib can only provide the lib - fail if
2073 // malformed
2074 if (lname == null || version < 0 || certSha256Digest == null) {
2075 return input.error("Bad uses-static-library declaration name: " + lname
2076 + " version: " + version + " certDigest" + certSha256Digest);
2077 }
2078
2079 // Can depend only on one version of the same library
2080 List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
2081 if (usesStaticLibraries.contains(lname)) {
2082 return input.error(
2083 "Depending on multiple versions of static library " + lname);
2084 }
2085
2086 lname = lname.intern();
2087 // We allow ":" delimiters in the SHA declaration as this is the format
2088 // emitted by the certtool making it easy for developers to copy/paste.
2089 certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2090
2091 // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
2092 String[] additionalCertSha256Digests = EmptyArray.STRING;
2093 if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
2094 ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
2095 if (certResult.isError()) {
2096 return input.error(certResult);
2097 }
2098 additionalCertSha256Digests = certResult.getResult();
2099 }
2100
2101 final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
2102 certSha256Digests[0] = certSha256Digest;
2103 System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
2104 1, additionalCertSha256Digests.length);
2105
2106 return input.success(pkg.addUsesStaticLibrary(lname)
2107 .addUsesStaticLibraryVersion(version)
2108 .addUsesStaticLibraryCertDigests(certSha256Digests));
2109 } finally {
2110 sa.recycle();
2111 }
2112 }
2113
2114 @NonNull
2115 private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
2116 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2117 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
2118 try {
2119 // Note: don't allow this value to be a reference to a resource
2120 // that may change.
2121 String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
2122 boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
2123
2124 if (lname != null) {
2125 lname = lname.intern();
2126 if (req) {
2127 // Upgrade to treat as stronger constraint
2128 pkg.addUsesLibrary(lname)
2129 .removeUsesOptionalLibrary(lname);
2130 } else {
2131 // Ignore if someone already defined as required
2132 if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
2133 pkg.addUsesOptionalLibrary(lname);
2134 }
2135 }
2136 }
2137
2138 return input.success(pkg);
2139 } finally {
2140 sa.recycle();
2141 }
2142 }
2143
2144 @NonNull
2145 private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
2146 Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
2147 throws IOException, XmlPullParserException {
2148 ParseResult<ArrayMap<String, ParsedProcess>> result =
2149 ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
2150 input);
2151 if (result.isError()) {
2152 return input.error(result);
2153 }
2154
2155 return input.success(pkg.setProcesses(result.getResult()));
2156 }
2157
2158 @NonNull
2159 private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
2160 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2161 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
2162 try {
2163 return input.success(pkg.setProfileableByShell(pkg.isProfileableByShell()
2164 || bool(false, R.styleable.AndroidManifestProfileable_shell, sa)));
2165 } finally {
2166 sa.recycle();
2167 }
2168 }
2169
2170 private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
2171 Resources resources, XmlResourceParser parser)
2172 throws XmlPullParserException, IOException {
2173 String[] certSha256Digests = EmptyArray.STRING;
2174 final int depth = parser.getDepth();
Winson14ff7172019-10-23 10:42:27 -07002175 int type;
2176 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
Winsonf00c7552020-01-28 12:52:01 -08002177 && (type != XmlPullParser.END_TAG
2178 || parser.getDepth() > depth)) {
2179 if (type != XmlPullParser.START_TAG) {
Winson14ff7172019-10-23 10:42:27 -07002180 continue;
2181 }
2182
2183 final String nodeName = parser.getName();
2184 if (nodeName.equals("additional-certificate")) {
Winsonf00c7552020-01-28 12:52:01 -08002185 TypedArray sa = resources.obtainAttributes(parser,
Winson14ff7172019-10-23 10:42:27 -07002186 R.styleable.AndroidManifestAdditionalCertificate);
Winsonf00c7552020-01-28 12:52:01 -08002187 try {
2188 String certSha256Digest = sa.getNonResourceString(
2189 R.styleable.AndroidManifestAdditionalCertificate_certDigest);
Winson14ff7172019-10-23 10:42:27 -07002190
Winsonf00c7552020-01-28 12:52:01 -08002191 if (TextUtils.isEmpty(certSha256Digest)) {
2192 return input.error("Bad additional-certificate declaration with empty"
2193 + " certDigest:" + certSha256Digest);
2194 }
2195
2196
2197 // We allow ":" delimiters in the SHA declaration as this is the format
2198 // emitted by the certtool making it easy for developers to copy/paste.
2199 certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
2200 certSha256Digests = ArrayUtils.appendElement(String.class,
2201 certSha256Digests, certSha256Digest);
2202 } finally {
Winson14ff7172019-10-23 10:42:27 -07002203 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07002204 }
Winson14ff7172019-10-23 10:42:27 -07002205 }
2206 }
2207
Winsonf00c7552020-01-28 12:52:01 -08002208 return input.success(certSha256Digests);
Winson14ff7172019-10-23 10:42:27 -07002209 }
2210
2211 /**
2212 * Generate activity object that forwards user to App Details page automatically.
2213 * This activity should be invisible to user and user should not know or see it.
Winson14ff7172019-10-23 10:42:27 -07002214 */
2215 @NonNull
Winsonf00c7552020-01-28 12:52:01 -08002216 private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
2217 ParsingPackage pkg) {
2218 String packageName = pkg.getPackageName();
Winson727da642020-03-10 15:25:32 -07002219 ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName(
Winsonf00c7552020-01-28 12:52:01 -08002220 packageName, packageName, ":app_details", input);
Winson727da642020-03-10 15:25:32 -07002221 if (result.isError()) {
2222 return input.error(result);
Winson14ff7172019-10-23 10:42:27 -07002223 }
2224
Winson727da642020-03-10 15:25:32 -07002225 String taskAffinity = result.getResult();
2226
Winsonf00c7552020-01-28 12:52:01 -08002227 // Build custom App Details activity info instead of parsing it from xml
2228 return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
2229 pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
2230 pkg.isBaseHardwareAccelerated()));
Winson14ff7172019-10-23 10:42:27 -07002231 }
2232
2233 /**
2234 * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
2235 */
Winsonf00c7552020-01-28 12:52:01 -08002236 private static boolean hasDomainURLs(ParsingPackage pkg) {
2237 final List<ParsedActivity> activities = pkg.getActivities();
2238 final int activitiesSize = activities.size();
2239 for (int index = 0; index < activitiesSize; index++) {
2240 ParsedActivity activity = activities.get(index);
2241 List<ParsedIntentInfo> filters = activity.getIntents();
2242 final int filtersSize = filters.size();
2243 for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
2244 ParsedIntentInfo aii = filters.get(filtersIndex);
Winson14ff7172019-10-23 10:42:27 -07002245 if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
2246 if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
2247 if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
2248 aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
2249 return true;
2250 }
2251 }
2252 }
2253 return false;
2254 }
2255
2256 /**
2257 * Sets the max aspect ratio of every child activity that doesn't already have an aspect
2258 * ratio set.
2259 */
Winsonf00c7552020-01-28 12:52:01 -08002260 private static void setMaxAspectRatio(ParsingPackage pkg) {
Winson14ff7172019-10-23 10:42:27 -07002261 // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
2262 // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
Winsonf00c7552020-01-28 12:52:01 -08002263 float maxAspectRatio = pkg.getTargetSdkVersion() < O
Winson14ff7172019-10-23 10:42:27 -07002264 ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
2265
Winsonf00c7552020-01-28 12:52:01 -08002266 float packageMaxAspectRatio = pkg.getMaxAspectRatio();
Winson14ff7172019-10-23 10:42:27 -07002267 if (packageMaxAspectRatio != 0) {
2268 // Use the application max aspect ration as default if set.
2269 maxAspectRatio = packageMaxAspectRatio;
2270 } else {
Winsonf00c7552020-01-28 12:52:01 -08002271 Bundle appMetaData = pkg.getMetaData();
Winson14ff7172019-10-23 10:42:27 -07002272 if (appMetaData != null && appMetaData.containsKey(
2273 PackageParser.METADATA_MAX_ASPECT_RATIO)) {
2274 maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
2275 maxAspectRatio);
2276 }
2277 }
2278
Winsonf00c7552020-01-28 12:52:01 -08002279 List<ParsedActivity> activities = pkg.getActivities();
2280 int activitiesSize = activities.size();
2281 for (int index = 0; index < activitiesSize; index++) {
2282 ParsedActivity activity = activities.get(index);
2283 // If the max aspect ratio for the activity has already been set, skip.
2284 if (activity.getMaxAspectRatio() != null) {
2285 continue;
Winson14ff7172019-10-23 10:42:27 -07002286 }
Winsonf00c7552020-01-28 12:52:01 -08002287
2288 // By default we prefer to use a values defined on the activity directly than values
2289 // defined on the application. We do not check the styled attributes on the activity
2290 // as it would have already been set when we processed the activity. We wait to
2291 // process the meta data here since this method is called at the end of processing
2292 // the application and all meta data is guaranteed.
2293 final float activityAspectRatio = activity.getMetaData() != null
2294 ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
2295 maxAspectRatio)
2296 : maxAspectRatio;
2297
2298 activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
Winson14ff7172019-10-23 10:42:27 -07002299 }
2300 }
2301
2302 /**
2303 * Sets the min aspect ratio of every child activity that doesn't already have an aspect
2304 * ratio set.
2305 */
Winsonf00c7552020-01-28 12:52:01 -08002306 private void setMinAspectRatio(ParsingPackage pkg) {
Winson14ff7172019-10-23 10:42:27 -07002307 final float minAspectRatio;
Winsonf00c7552020-01-28 12:52:01 -08002308 float packageMinAspectRatio = pkg.getMinAspectRatio();
Winson14ff7172019-10-23 10:42:27 -07002309 if (packageMinAspectRatio != 0) {
2310 // Use the application max aspect ration as default if set.
2311 minAspectRatio = packageMinAspectRatio;
2312 } else {
2313 // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
2314 // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
2315 // except for watches which always supported 1:1.
Winsonf00c7552020-01-28 12:52:01 -08002316 minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q
Winson14ff7172019-10-23 10:42:27 -07002317 ? 0
Winsonf00c7552020-01-28 12:52:01 -08002318 : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
Winson14ff7172019-10-23 10:42:27 -07002319 ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
2320 : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
2321 }
2322
Winsonf00c7552020-01-28 12:52:01 -08002323 List<ParsedActivity> activities = pkg.getActivities();
2324 int activitiesSize = activities.size();
2325 for (int index = 0; index < activitiesSize; index++) {
2326 ParsedActivity activity = activities.get(index);
2327 if (activity.getMinAspectRatio() == null) {
2328 activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
Winson14ff7172019-10-23 10:42:27 -07002329 }
2330 }
2331 }
2332
Winsonf00c7552020-01-28 12:52:01 -08002333 private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
2334 Resources res, XmlResourceParser parser) {
Winson14ff7172019-10-23 10:42:27 -07002335 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
Winsonf00c7552020-01-28 12:52:01 -08002336 try {
2337 String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
2338 int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
Winson14ff7172019-10-23 10:42:27 -07002339
Winsonf00c7552020-01-28 12:52:01 -08002340 if (target == null) {
2341 return input.error("<overlay> does not specify a target package");
2342 } else if (priority < 0 || priority > 9999) {
2343 return input.error("<overlay> priority must be between 0 and 9999");
Winson14ff7172019-10-23 10:42:27 -07002344 }
Winsonf00c7552020-01-28 12:52:01 -08002345
2346 // check to see if overlay should be excluded based on system property condition
2347 String propName = sa.getString(
2348 R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
2349 String propValue = sa.getString(
2350 R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
2351 if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
2352 Slog.i(TAG, "Skipping target and overlay pair " + target + " and "
2353 + pkg.getBaseCodePath()
2354 + ": overlay ignored due to required system property: "
2355 + propName + " with value: " + propValue);
2356 return input.error("Skipping target and overlay pair " + target + " and "
2357 + pkg.getBaseCodePath()
2358 + ": overlay ignored due to required system property: "
2359 + propName + " with value: " + propValue);
2360 }
2361
2362 return input.success(pkg.setOverlay(true)
2363 .setOverlayTarget(target)
2364 .setOverlayPriority(priority)
2365 .setOverlayTargetName(
2366 sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
2367 .setOverlayCategory(
2368 sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
2369 .setOverlayIsStatic(
2370 bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
2371 } finally {
2372 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07002373 }
Winson14ff7172019-10-23 10:42:27 -07002374 }
2375
Winsonf00c7552020-01-28 12:52:01 -08002376 private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
2377 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2378 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
2379 try {
2380 // Note: don't allow this value to be a reference to a resource
2381 // that may change.
2382 String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
2383 if (name != null) {
2384 pkg.addProtectedBroadcast(name);
2385 }
2386 return input.success(pkg);
2387 } finally {
2388 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07002389 }
Winson14ff7172019-10-23 10:42:27 -07002390 }
2391
Winsonf00c7552020-01-28 12:52:01 -08002392 private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
2393 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2394 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
2395 try {
2396 int requiresSmallestWidthDp = anInt(0,
2397 R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
2398 int compatibleWidthLimitDp = anInt(0,
2399 R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
2400 int largestWidthLimitDp = anInt(0,
2401 R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
2402
2403 // This is a trick to get a boolean and still able to detect
2404 // if a value was actually set.
2405 return input.success(pkg
2406 .setSupportsSmallScreens(
2407 anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
2408 .setSupportsNormalScreens(
2409 anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
2410 .setSupportsLargeScreens(
2411 anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
2412 .setSupportsExtraLargeScreens(
2413 anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
2414 .setResizeable(
2415 anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
2416 .setAnyDensity(
2417 anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
2418 .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
2419 .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
2420 .setLargestWidthLimitDp(largestWidthLimitDp));
2421 } finally {
2422 sa.recycle();
2423 }
2424 }
2425
2426 private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
2427 ParsingPackage pkg, Resources res, XmlResourceParser parser)
2428 throws XmlPullParserException, IOException {
2429 ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
2430 pkg, res, parser, PackageParser.sUseRoundIcon, input);
2431 if (result.isError()) {
2432 return input.error(result);
2433 }
2434 return input.success(pkg.addInstrumentation(result.getResult()));
2435 }
2436
2437 private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
2438 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2439 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
2440 try {
2441 String orig = sa.getNonConfigurationString(
2442 R.styleable.AndroidManifestOriginalPackage_name,
2443 0);
2444 if (!pkg.getPackageName().equals(orig)) {
2445 if (pkg.getOriginalPackages().isEmpty()) {
2446 pkg.setRealPackage(pkg.getPackageName());
2447 }
2448 pkg.addOriginalPackage(orig);
2449 }
2450 return input.success(pkg);
2451 } finally {
2452 sa.recycle();
2453 }
2454 }
2455
2456 private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
2457 ParsingPackage pkg, Resources res, XmlResourceParser parser) {
2458 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
2459 try {
2460 String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
2461 if (name != null) {
2462 pkg.addAdoptPermission(name);
2463 }
2464 return input.success(pkg);
2465 } finally {
2466 sa.recycle();
2467 }
2468 }
2469
2470 private static void convertNewPermissions(ParsingPackage pkg) {
Winson14ff7172019-10-23 10:42:27 -07002471 final int NP = PackageParser.NEW_PERMISSIONS.length;
2472 StringBuilder newPermsMsg = null;
2473 for (int ip = 0; ip < NP; ip++) {
2474 final PackageParser.NewPermissionInfo npi
2475 = PackageParser.NEW_PERMISSIONS[ip];
Winsonf00c7552020-01-28 12:52:01 -08002476 if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
Winson14ff7172019-10-23 10:42:27 -07002477 break;
2478 }
Winsonf00c7552020-01-28 12:52:01 -08002479 if (!pkg.getRequestedPermissions().contains(npi.name)) {
Winson14ff7172019-10-23 10:42:27 -07002480 if (newPermsMsg == null) {
2481 newPermsMsg = new StringBuilder(128);
Winsonf00c7552020-01-28 12:52:01 -08002482 newPermsMsg.append(pkg.getPackageName());
Winson14ff7172019-10-23 10:42:27 -07002483 newPermsMsg.append(": compat added ");
2484 } else {
2485 newPermsMsg.append(' ');
2486 }
2487 newPermsMsg.append(npi.name);
Winsonf00c7552020-01-28 12:52:01 -08002488 pkg.addRequestedPermission(npi.name)
2489 .addImplicitPermission(npi.name);
Winson14ff7172019-10-23 10:42:27 -07002490 }
2491 }
2492 if (newPermsMsg != null) {
2493 Slog.i(TAG, newPermsMsg.toString());
2494 }
2495 }
2496
Winsonf00c7552020-01-28 12:52:01 -08002497 private static void convertSplitPermissions(ParsingPackage pkg) {
Winson14ff7172019-10-23 10:42:27 -07002498 List<SplitPermissionInfoParcelable> splitPermissions;
2499
2500 try {
2501 splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
2502 } catch (RemoteException e) {
2503 throw e.rethrowFromSystemServer();
2504 }
2505
2506 final int listSize = splitPermissions.size();
2507 for (int is = 0; is < listSize; is++) {
2508 final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
Winsonf00c7552020-01-28 12:52:01 -08002509 List<String> requestedPermissions = pkg.getRequestedPermissions();
2510 if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
Winson14ff7172019-10-23 10:42:27 -07002511 || !requestedPermissions.contains(spi.getSplitPermission())) {
2512 continue;
2513 }
2514 final List<String> newPerms = spi.getNewPermissions();
2515 for (int in = 0; in < newPerms.size(); in++) {
2516 final String perm = newPerms.get(in);
2517 if (!requestedPermissions.contains(perm)) {
Winsonf00c7552020-01-28 12:52:01 -08002518 pkg.addRequestedPermission(perm)
2519 .addImplicitPermission(perm);
Winson14ff7172019-10-23 10:42:27 -07002520 }
2521 }
2522 }
2523 }
2524
2525 private static boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {
2526 if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
2527 if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
2528 // malformed condition - incomplete
2529 Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName
2530 + "=" + propValue + "' - require both requiredSystemPropertyName"
2531 + " AND requiredSystemPropertyValue to be specified.");
2532 return false;
2533 }
2534 // no valid condition set - so no exclusion criteria, overlay will be included.
2535 return true;
2536 }
2537
2538 // check property value - make sure it is both set and equal to expected value
2539 final String currValue = SystemProperties.get(propName);
2540 return (currValue != null && currValue.equals(propValue));
2541 }
2542
2543 /**
2544 * This is a pre-density application which will get scaled - instead of being pixel perfect.
2545 * This type of application is not resizable.
2546 *
Winsonf00c7552020-01-28 12:52:01 -08002547 * @param pkg The package which needs to be marked as unresizable.
Winson14ff7172019-10-23 10:42:27 -07002548 */
Winsonf00c7552020-01-28 12:52:01 -08002549 private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
2550 List<ParsedActivity> activities = pkg.getActivities();
2551 int activitiesSize = activities.size();
2552 for (int index = 0; index < activitiesSize; index++) {
2553 ParsedActivity activity = activities.get(index);
2554 activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
2555 .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
Winson14ff7172019-10-23 10:42:27 -07002556 }
2557 }
2558
Winsonf00c7552020-01-28 12:52:01 -08002559 private static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
Winson14ff7172019-10-23 10:42:27 -07002560 boolean requireFilename) {
2561 final int N = name.length();
2562 boolean hasSep = false;
2563 boolean front = true;
2564 for (int i = 0; i < N; i++) {
2565 final char c = name.charAt(i);
2566 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2567 front = false;
2568 continue;
2569 }
2570 if (!front) {
2571 if ((c >= '0' && c <= '9') || c == '_') {
2572 continue;
2573 }
2574 }
2575 if (c == '.') {
2576 hasSep = true;
2577 front = true;
2578 continue;
2579 }
Winsonf00c7552020-01-28 12:52:01 -08002580 return input.error("bad character '" + c + "'");
Winson14ff7172019-10-23 10:42:27 -07002581 }
2582 if (requireFilename && !FileUtils.isValidExtFilename(name)) {
Winsonf00c7552020-01-28 12:52:01 -08002583 return input.error("Invalid filename");
Winson14ff7172019-10-23 10:42:27 -07002584 }
2585 return hasSep || !requireSeparator
Winsonf00c7552020-01-28 12:52:01 -08002586 ? input.success(null)
2587 : input.error("must have at least one '.' separator");
Winson14ff7172019-10-23 10:42:27 -07002588 }
2589
Winsonf00c7552020-01-28 12:52:01 -08002590 public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res,
2591 XmlResourceParser parser, Bundle data, ParseInput input) {
2592 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
2593 try {
2594 if (data == null) {
2595 data = new Bundle();
Winson14ff7172019-10-23 10:42:27 -07002596 }
Winsonf00c7552020-01-28 12:52:01 -08002597
2598 String name = TextUtils.safeIntern(
2599 nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
2600 if (name == null) {
2601 return input.error("<meta-data> requires an android:name attribute");
2602 }
2603
2604 TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
2605 if (v != null && v.resourceId != 0) {
2606 //Slog.i(TAG, "Meta data ref " + name + ": " + v);
2607 data.putInt(name, v.resourceId);
2608 } else {
2609 v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
2610 //Slog.i(TAG, "Meta data " + name + ": " + v);
2611 if (v != null) {
2612 if (v.type == TypedValue.TYPE_STRING) {
2613 CharSequence cs = v.coerceToString();
2614 data.putString(name, cs != null ? cs.toString() : null);
2615 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
2616 data.putBoolean(name, v.data != 0);
2617 } else if (v.type >= TypedValue.TYPE_FIRST_INT
2618 && v.type <= TypedValue.TYPE_LAST_INT) {
2619 data.putInt(name, v.data);
2620 } else if (v.type == TypedValue.TYPE_FLOAT) {
2621 data.putFloat(name, v.getFloat());
2622 } else {
2623 if (!PackageParser.RIGID_PARSER) {
2624 Slog.w(TAG,
2625 "<meta-data> only supports string, integer, float, color, "
2626 + "boolean, and resource reference types: "
2627 + parser.getName() + " at "
2628 + pkg.getBaseCodePath() + " "
2629 + parser.getPositionDescription());
2630 } else {
2631 return input.error("<meta-data> only supports string, integer, float, "
2632 + "color, boolean, and resource reference types");
2633 }
2634 }
2635 } else {
2636 return input.error("<meta-data> requires an android:value "
2637 + "or android:resource attribute");
2638 }
2639 }
2640 return input.success(data);
2641 } finally {
2642 sa.recycle();
Winson14ff7172019-10-23 10:42:27 -07002643 }
Winson14ff7172019-10-23 10:42:27 -07002644 }
2645
2646 /**
Winsone23ae202020-01-24 11:56:44 -08002647 * Collect certificates from all the APKs described in the given package. Also asserts that
Winson14ff7172019-10-23 10:42:27 -07002648 * all APK contents are signed correctly and consistently.
2649 */
Winsone23ae202020-01-24 11:56:44 -08002650 public static SigningDetails collectCertificates(ParsingPackageRead pkg, boolean skipVerify)
Winson14ff7172019-10-23 10:42:27 -07002651 throws PackageParserException {
Winsone23ae202020-01-24 11:56:44 -08002652 SigningDetails signingDetails = SigningDetails.UNKNOWN;
Winson14ff7172019-10-23 10:42:27 -07002653
2654 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
2655 try {
Winsone23ae202020-01-24 11:56:44 -08002656 signingDetails = collectCertificates(
Winson14ff7172019-10-23 10:42:27 -07002657 pkg.getBaseCodePath(),
2658 skipVerify,
2659 pkg.isStaticSharedLibrary(),
Winsone23ae202020-01-24 11:56:44 -08002660 signingDetails,
Michael Groover33df7c42020-01-24 19:00:01 -08002661 pkg.getTargetSdkVersion()
Winsone23ae202020-01-24 11:56:44 -08002662 );
Winson14ff7172019-10-23 10:42:27 -07002663
2664 String[] splitCodePaths = pkg.getSplitCodePaths();
2665 if (!ArrayUtils.isEmpty(splitCodePaths)) {
2666 for (int i = 0; i < splitCodePaths.length; i++) {
Winsone23ae202020-01-24 11:56:44 -08002667 signingDetails = collectCertificates(
Winson14ff7172019-10-23 10:42:27 -07002668 splitCodePaths[i],
2669 skipVerify,
2670 pkg.isStaticSharedLibrary(),
Winsone23ae202020-01-24 11:56:44 -08002671 signingDetails,
Michael Groover33df7c42020-01-24 19:00:01 -08002672 pkg.getTargetSdkVersion()
Winsone23ae202020-01-24 11:56:44 -08002673 );
Winson14ff7172019-10-23 10:42:27 -07002674 }
2675 }
Winsone23ae202020-01-24 11:56:44 -08002676 return signingDetails;
Winson14ff7172019-10-23 10:42:27 -07002677 } finally {
2678 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
2679 }
2680 }
2681
Winsonf00c7552020-01-28 12:52:01 -08002682 public static SigningDetails collectCertificates(String baseCodePath, boolean skipVerify,
2683 boolean isStaticSharedLibrary, @NonNull SigningDetails existingSigningDetails,
2684 int targetSdk) throws PackageParserException {
Michael Groover33df7c42020-01-24 19:00:01 -08002685 int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
2686 targetSdk);
Winson14ff7172019-10-23 10:42:27 -07002687 if (isStaticSharedLibrary) {
2688 // must use v2 signing scheme
2689 minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
2690 }
2691 SigningDetails verified;
2692 if (skipVerify) {
2693 // systemDir APKs are already trusted, save time by not verifying
2694 verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
2695 baseCodePath, minSignatureScheme);
2696 } else {
2697 verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
2698 }
2699
2700 // Verify that entries are signed consistently with the first pkg
2701 // we encountered. Note that for splits, certificates may have
2702 // already been populated during an earlier parse of a base APK.
2703 if (existingSigningDetails == SigningDetails.UNKNOWN) {
2704 return verified;
2705 } else {
2706 if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
2707 throw new PackageParserException(
2708 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
2709 baseCodePath + " has mismatched certificates");
2710 }
2711
2712 return existingSigningDetails;
2713 }
2714 }
2715
Winsonf00c7552020-01-28 12:52:01 -08002716 /*
2717 The following set of methods makes code easier to read by re-ordering the TypedArray methods.
2718
2719 The first parameter is the default, which is the most important to understand for someone
2720 reading through the parsing code.
2721
2722 That's followed by the attribute name, which is usually irrelevant during reading because
2723 it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
2724 the "setSomeValue" part is enough to communicate what the line does.
2725
2726 Last comes the TypedArray, which is by far the least important since each try-with-resources
2727 should only have 1.
2728 */
2729
2730 // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
2731 // is important to specify when adding an attribute.
2732 private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
2733 return sa.getBoolean(attribute, defaultValue);
Winson14ff7172019-10-23 10:42:27 -07002734 }
2735
Winsonf00c7552020-01-28 12:52:01 -08002736 private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
2737 return sa.getFloat(attribute, defaultValue);
Winson14ff7172019-10-23 10:42:27 -07002738 }
2739
Winsonf00c7552020-01-28 12:52:01 -08002740 private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
2741 return sa.getFloat(attribute, 0f);
2742 }
Winson14ff7172019-10-23 10:42:27 -07002743
Winsonf00c7552020-01-28 12:52:01 -08002744 private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
2745 return sa.getInt(attribute, defaultValue);
2746 }
Winson14ff7172019-10-23 10:42:27 -07002747
Todd Kennedy83eddae2020-03-02 09:21:25 -08002748 private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
2749 return sa.getInteger(attribute, defaultValue);
2750 }
2751
Winsonf00c7552020-01-28 12:52:01 -08002752 private static int anInt(@StyleableRes int attribute, TypedArray sa) {
2753 return sa.getInt(attribute, 0);
2754 }
Winson14ff7172019-10-23 10:42:27 -07002755
Winsonf00c7552020-01-28 12:52:01 -08002756 @AnyRes
2757 private static int resId(@StyleableRes int attribute, TypedArray sa) {
2758 return sa.getResourceId(attribute, 0);
2759 }
Winson14ff7172019-10-23 10:42:27 -07002760
Winsonf00c7552020-01-28 12:52:01 -08002761 private static String string(@StyleableRes int attribute, TypedArray sa) {
2762 return sa.getString(attribute);
2763 }
Winson14ff7172019-10-23 10:42:27 -07002764
Winsonf00c7552020-01-28 12:52:01 -08002765 private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
2766 TypedArray sa) {
2767 return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
2768 }
Winson14ff7172019-10-23 10:42:27 -07002769
Winsonf00c7552020-01-28 12:52:01 -08002770 private static String nonResString(@StyleableRes int index, TypedArray sa) {
2771 return sa.getNonResourceString(index);
Winson14ff7172019-10-23 10:42:27 -07002772 }
Winsone23ae202020-01-24 11:56:44 -08002773
2774 /**
2775 * Callback interface for retrieving information that may be needed while parsing
2776 * a package.
2777 */
2778 public interface Callback {
2779 boolean hasFeature(String feature);
2780
Winson727da642020-03-10 15:25:32 -07002781 ParsingPackage startParsingPackage(@NonNull String packageName,
2782 @NonNull String baseCodePath, @NonNull String codePath,
Todd Kennedy83eddae2020-03-02 09:21:25 -08002783 @NonNull TypedArray manifestArray, boolean isCoreApp);
Winsone23ae202020-01-24 11:56:44 -08002784 }
Winson14ff7172019-10-23 10:42:27 -07002785}