Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1 | /* |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2 | * Copyright (C) 2020 The Android Open Source Project |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.content.pm.parsing; |
| 18 | |
| 19 | import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 20 | import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 21 | import static android.content.pm.PackageManager.FEATURE_WATCH; |
| 22 | import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; |
| 23 | import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; |
Rhed Jao | 269616b | 2020-03-30 13:23:14 +0800 | [diff] [blame] | 24 | import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED; |
Ryan Mitchell | 192400c | 2020-04-02 09:54:23 -0700 | [diff] [blame] | 25 | import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 26 | import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 27 | import static android.os.Build.VERSION_CODES.DONUT; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 28 | import static android.os.Build.VERSION_CODES.O; |
| 29 | import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 30 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 31 | import android.annotation.AnyRes; |
| 32 | import android.annotation.IntRange; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 33 | import android.annotation.NonNull; |
| 34 | import android.annotation.Nullable; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 35 | import android.annotation.StyleableRes; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 36 | import android.app.ActivityThread; |
| 37 | import android.content.Intent; |
| 38 | import android.content.IntentFilter; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 39 | import android.content.pm.ApplicationInfo; |
| 40 | import android.content.pm.ConfigurationInfo; |
| 41 | import android.content.pm.FeatureGroupInfo; |
| 42 | import android.content.pm.FeatureInfo; |
| 43 | import android.content.pm.PackageInfo; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 44 | import android.content.pm.PackageManager; |
| 45 | import android.content.pm.PackageParser; |
| 46 | import android.content.pm.PackageParser.PackageParserException; |
| 47 | import android.content.pm.PackageParser.SigningDetails; |
| 48 | import android.content.pm.Signature; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 49 | import android.content.pm.parsing.component.ComponentParseUtils; |
| 50 | import android.content.pm.parsing.component.ParsedActivity; |
| 51 | import android.content.pm.parsing.component.ParsedActivityUtils; |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame] | 52 | import android.content.pm.parsing.component.ParsedAttribution; |
| 53 | import android.content.pm.parsing.component.ParsedAttributionUtils; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 54 | import android.content.pm.parsing.component.ParsedInstrumentation; |
| 55 | import android.content.pm.parsing.component.ParsedInstrumentationUtils; |
| 56 | import android.content.pm.parsing.component.ParsedIntentInfo; |
| 57 | import android.content.pm.parsing.component.ParsedIntentInfoUtils; |
| 58 | import android.content.pm.parsing.component.ParsedMainComponent; |
| 59 | import android.content.pm.parsing.component.ParsedPermission; |
| 60 | import android.content.pm.parsing.component.ParsedPermissionGroup; |
| 61 | import android.content.pm.parsing.component.ParsedPermissionUtils; |
| 62 | import android.content.pm.parsing.component.ParsedProcess; |
| 63 | import android.content.pm.parsing.component.ParsedProcessUtils; |
| 64 | import android.content.pm.parsing.component.ParsedProvider; |
| 65 | import android.content.pm.parsing.component.ParsedProviderUtils; |
| 66 | import android.content.pm.parsing.component.ParsedService; |
| 67 | import android.content.pm.parsing.component.ParsedServiceUtils; |
| 68 | import android.content.pm.parsing.result.ParseInput; |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 69 | import android.content.pm.parsing.result.ParseInput.DeferredError; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 70 | import android.content.pm.parsing.result.ParseResult; |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 71 | import android.content.pm.parsing.result.ParseTypeImpl; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 72 | import android.content.pm.permission.SplitPermissionInfoParcelable; |
| 73 | import android.content.pm.split.DefaultSplitAssetLoader; |
| 74 | import android.content.pm.split.SplitAssetDependencyLoader; |
| 75 | import android.content.pm.split.SplitAssetLoader; |
Winson Chiu | 2fdaf81 | 2019-12-13 20:01:15 +0000 | [diff] [blame] | 76 | import android.content.res.ApkAssets; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 77 | import android.content.res.AssetManager; |
| 78 | import android.content.res.Configuration; |
| 79 | import android.content.res.Resources; |
| 80 | import android.content.res.TypedArray; |
| 81 | import android.content.res.XmlResourceParser; |
| 82 | import android.net.Uri; |
| 83 | import android.os.Build; |
| 84 | import android.os.Bundle; |
| 85 | import android.os.FileUtils; |
| 86 | import android.os.RemoteException; |
| 87 | import android.os.SystemProperties; |
| 88 | import android.os.Trace; |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 89 | import android.os.ext.SdkExtensions; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 90 | import android.text.TextUtils; |
| 91 | import android.util.ArrayMap; |
| 92 | import android.util.ArraySet; |
| 93 | import android.util.AttributeSet; |
| 94 | import android.util.DisplayMetrics; |
| 95 | import android.util.Pair; |
| 96 | import android.util.Slog; |
| 97 | import android.util.SparseArray; |
Oli Lan | 72ae447 | 2020-04-14 16:50:41 +0100 | [diff] [blame] | 98 | import android.util.SparseIntArray; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 99 | import android.util.TypedValue; |
| 100 | import android.util.apk.ApkSignatureVerifier; |
| 101 | |
| 102 | import com.android.internal.R; |
| 103 | import com.android.internal.os.ClassLoaderFactory; |
| 104 | import com.android.internal.util.ArrayUtils; |
| 105 | import com.android.internal.util.XmlUtils; |
| 106 | |
| 107 | import libcore.io.IoUtils; |
| 108 | import libcore.util.EmptyArray; |
| 109 | |
| 110 | import org.xmlpull.v1.XmlPullParser; |
| 111 | import org.xmlpull.v1.XmlPullParserException; |
| 112 | |
| 113 | import java.io.File; |
| 114 | import java.io.IOException; |
| 115 | import java.security.PublicKey; |
| 116 | import java.util.ArrayList; |
| 117 | import java.util.Arrays; |
| 118 | import java.util.List; |
Winson Chiu | 2fdaf81 | 2019-12-13 20:01:15 +0000 | [diff] [blame] | 119 | import java.util.Map; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 120 | import java.util.Set; |
Patrick Baumann | 9918123 | 2020-01-28 10:55:25 -0800 | [diff] [blame] | 121 | import java.util.StringTokenizer; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 122 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 123 | /** |
| 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 | */ |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 129 | public class ParsingPackageUtils { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 130 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 131 | public static final String TAG = ParsingUtils.TAG; |
| 132 | |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 133 | /** |
| 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 178 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 190 | |
| 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 |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 201 | * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}. |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 202 | * |
| 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 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 210 | public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, |
| 211 | int flags) |
| 212 | throws PackageParserException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 213 | if (packageFile.isDirectory()) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 214 | return parseClusterPackage(input, packageFile, flags); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 215 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 216 | return parseMonolithicPackage(input, packageFile, flags); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 217 | } |
| 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 |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 227 | * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}. |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 228 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 229 | private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir, |
| 230 | int flags) throws PackageParserException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 231 | final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir, |
| 232 | 0); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 233 | if (mOnlyCoreApps && !lite.coreApp) { |
Rhed Jao | 269616b | 2020-03-30 13:23:14 +0800 | [diff] [blame] | 234 | return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, |
| 235 | "Not a coreApp: " + packageDir); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 236 | } |
| 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) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 246 | return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 247 | } |
| 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); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 255 | ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk, |
| 256 | lite.codePath, assets, flags); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 257 | if (result.isError()) { |
Winson | 36c4470 | 2020-04-09 13:48:45 -0700 | [diff] [blame] | 258 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 259 | } |
| 260 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 261 | ParsingPackage pkg = result.getResult(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 262 | if (!ArrayUtils.isEmpty(lite.splitNames)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 263 | pkg.asSplit( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 264 | 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); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 273 | parseSplitApk(input, pkg, i, splitAssets, flags); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 274 | } |
| 275 | } |
| 276 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 277 | pkg.setUse32BitAbi(lite.use32bitAbi); |
| 278 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 279 | } 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 |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 288 | * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}. |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 289 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 290 | private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile, |
| 291 | int flags) throws PackageParserException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 292 | final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile, |
| 293 | flags); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 294 | if (mOnlyCoreApps && !lite.coreApp) { |
Rhed Jao | 269616b | 2020-03-30 13:23:14 +0800 | [diff] [blame] | 295 | return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, |
| 296 | "Not a coreApp: " + apkFile); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); |
| 300 | try { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 301 | 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)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 311 | } catch (IOException e) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 312 | return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 313 | "Failed to get path: " + apkFile, e); |
| 314 | } finally { |
| 315 | IoUtils.closeQuietly(assetLoader); |
| 316 | } |
| 317 | } |
| 318 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 319 | private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile, |
| 320 | String codePath, AssetManager assets, int flags) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 321 | 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 331 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 336 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 337 | 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(), |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 345 | apkPath + " (at " + parser.getPositionDescription() + "): " |
| 346 | + result.getErrorMessage()); |
| 347 | } |
| 348 | |
Ryan Mitchell | 192400c | 2020-04-02 09:54:23 -0700 | [diff] [blame] | 349 | 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 Chiu | 2fdaf81 | 2019-12-13 20:01:15 +0000 | [diff] [blame] | 363 | 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 378 | pkg.setVolumeUuid(volumeUuid) |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 379 | .setSigningDetails(SigningDetails.UNKNOWN); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 380 | |
| 381 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 382 | } catch (Exception e) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 383 | return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 384 | "Failed to read manifest from " + apkPath, e); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 385 | } |
| 386 | } |
| 387 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 388 | private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, |
| 389 | ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) { |
| 390 | final String apkPath = pkg.getSplitCodePaths()[splitIndex]; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 391 | |
| 392 | if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); |
| 393 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 394 | // 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(), |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 407 | apkPath + " (at " + parser.getPositionDescription() + "): " |
| 408 | + parseResult.getErrorMessage()); |
| 409 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 410 | |
| 411 | return parseResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 412 | } catch (Exception e) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 413 | return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 414 | "Failed to read manifest from " + apkPath, e); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 415 | } |
| 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 | * |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 423 | * @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 |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 427 | * @return Parsed package or null on error. |
| 428 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 429 | private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath, |
| 430 | String codePath, Resources res, XmlResourceParser parser, int flags) |
| 431 | throws XmlPullParserException, IOException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 432 | 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)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 442 | return input.error( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 443 | PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, |
| 444 | "Expected base APK, but found split " + splitName |
| 445 | ); |
| 446 | } |
| 447 | } catch (PackageParserException e) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 448 | return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 449 | } |
| 450 | |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 451 | final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 452 | try { |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 453 | 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); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 459 | if (result.isError()) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 460 | return result; |
| 461 | } |
| 462 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 463 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 464 | } finally { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 465 | manifestArray.recycle(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 466 | } |
| 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 | * |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 476 | * @param pkg builder to fill |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 477 | * @return false on failure |
| 478 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 479 | private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg, |
| 480 | Resources res, XmlResourceParser parser, int flags, int splitIndex) |
| 481 | throws XmlPullParserException, IOException, PackageParserException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 482 | 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(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 492 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { |
| 493 | if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 494 | continue; |
| 495 | } |
| 496 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 497 | final ParseResult result; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 498 | String tagName = parser.getName(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 499 | if (PackageParser.TAG_APPLICATION.equals(tagName)) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 500 | if (foundApp) { |
| 501 | if (PackageParser.RIGID_PARSER) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 502 | result = input.error("<manifest> has more than one <application>"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 503 | } else { |
| 504 | Slog.w(TAG, "<manifest> has more than one <application>"); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 505 | result = input.success(null); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 506 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 507 | } else { |
| 508 | foundApp = true; |
| 509 | result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 510 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 511 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 512 | result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input); |
| 513 | } |
| 514 | |
| 515 | if (result.isError()) { |
| 516 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 517 | } |
| 518 | } |
| 519 | |
| 520 | if (!foundApp) { |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 521 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 526 | } |
| 527 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 528 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 529 | } |
| 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 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 539 | private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input, |
| 540 | ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex) |
| 541 | throws XmlPullParserException, IOException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 542 | TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 543 | try { |
| 544 | pkg.setSplitHasCode(splitIndex, sa.getBoolean( |
| 545 | R.styleable.AndroidManifestApplication_hasCode, true)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 546 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 547 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 557 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 558 | final int depth = parser.getDepth(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 559 | int type; |
| 560 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 561 | && (type != XmlPullParser.END_TAG |
| 562 | || parser.getDepth() > depth)) { |
| 563 | if (type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 564 | continue; |
| 565 | } |
| 566 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 567 | ParsedMainComponent mainComponent = null; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 568 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 569 | final ParseResult result; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 570 | String tagName = parser.getName(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 571 | boolean isActivity = false; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 572 | switch (tagName) { |
| 573 | case "activity": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 574 | isActivity = true; |
| 575 | // fall-through |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 576 | case "receiver": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 577 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 589 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 590 | result = activityResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 591 | break; |
| 592 | case "service": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 593 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 600 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 601 | result = serviceResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 602 | break; |
| 603 | case "provider": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 604 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 611 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 612 | result = providerResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 613 | break; |
| 614 | case "activity-alias": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 615 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 621 | } |
| 622 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 623 | result = activityResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 624 | break; |
| 625 | default: |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 626 | result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser); |
| 627 | break; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 628 | } |
| 629 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 630 | if (result.isError()) { |
| 631 | return input.error(result); |
| 632 | } |
| 633 | |
| 634 | if (mainComponent != null && mainComponent.getSplitName() == null) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 635 | // 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. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 639 | mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 640 | } |
| 641 | } |
| 642 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 643 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 644 | } |
| 645 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 646 | /** |
| 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 676 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 677 | 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()) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 682 | return sharedUserResult; |
| 683 | } |
| 684 | |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 685 | pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION, |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 686 | R.styleable.AndroidManifest_installLocation, sa)) |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 687 | .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX, |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 688 | R.styleable.AndroidManifest_targetSandboxVersion, sa)) |
| 689 | /* Set the global "on SD card" flag */ |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 690 | .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 691 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 692 | boolean foundApp = false; |
| 693 | final int depth = parser.getDepth(); |
| 694 | int type; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 695 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 696 | && (type != XmlPullParser.END_TAG |
| 697 | || parser.getDepth() > depth)) { |
| 698 | if (type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 699 | continue; |
| 700 | } |
| 701 | |
| 702 | String tagName = parser.getName(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 703 | final ParseResult result; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 704 | |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 705 | // TODO(b/135203078): Convert to instance methods to share variables |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 706 | // <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>"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 711 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 712 | Slog.w(TAG, "<manifest> has more than one <application>"); |
| 713 | result = input.success(null); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 714 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 715 | } else { |
| 716 | foundApp = true; |
| 717 | result = parseBaseApplication(input, pkg, res, parser, flags); |
| 718 | } |
| 719 | } else { |
| 720 | result = parseBaseApkTag(tagName, input, pkg, res, parser, flags); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 721 | } |
| 722 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 723 | if (result.isError()) { |
| 724 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 725 | } |
| 726 | } |
| 727 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 728 | if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) { |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 729 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 735 | } |
| 736 | |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame] | 737 | if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 738 | return input.error( |
Philip P. Moltmann | 9046d82 | 2019-12-13 15:59:49 -0800 | [diff] [blame] | 739 | INSTALL_PARSE_FAILED_BAD_MANIFEST, |
| 740 | "Combination <feature> tags are not valid" |
| 741 | ); |
| 742 | } |
| 743 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 744 | convertNewPermissions(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 745 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 746 | convertSplitPermissions(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 747 | |
| 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. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 751 | if (pkg.getTargetSdkVersion() < DONUT |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 752 | || (!pkg.isSupportsSmallScreens() |
| 753 | && !pkg.isSupportsNormalScreens() |
| 754 | && !pkg.isSupportsLargeScreens() |
| 755 | && !pkg.isSupportsExtraLargeScreens() |
| 756 | && !pkg.isResizeable() |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 757 | && !pkg.isAnyDensity())) { |
| 758 | adjustPackageToBeUnresizeableAndUnpipable(pkg); |
| 759 | } |
| 760 | |
| 761 | return input.success(pkg); |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 762 | } |
| 763 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 764 | 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. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame] | 772 | case "feature": // TODO moltmann: Remove |
| 773 | case PackageParser.TAG_ATTRIBUTION: |
| 774 | return parseAttribution(input, pkg, res, parser); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 775 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 816 | } |
| 817 | } |
| 818 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 819 | private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input, |
| 820 | ParsingPackage pkg, TypedArray sa) { |
| 821 | String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 822 | if (TextUtils.isEmpty(str)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 823 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 824 | } |
| 825 | |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 826 | if (!"android".equals(pkg.getPackageName())) { |
| 827 | ParseResult<?> nameResult = validateName(input, str, true, true); |
| 828 | if (nameResult.isError()) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 829 | return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, |
| 830 | "<manifest> specifies bad sharedUserId name \"" + str + "\": " |
| 831 | + nameResult.getErrorMessage()); |
| 832 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 833 | } |
| 834 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 835 | return input.success(pkg |
| 836 | .setSharedUserId(str.intern()) |
| 837 | .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa))); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 838 | } |
| 839 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 840 | private static ParseResult<ParsingPackage> parseKeySets(ParseInput input, |
| 841 | ParsingPackage pkg, Resources res, XmlResourceParser parser) |
| 842 | throws XmlPullParserException, IOException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 843 | // 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<>(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 852 | ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 853 | 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(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 864 | switch (tagName) { |
| 865 | case "key-set": { |
| 866 | if (currentKeySet != null) { |
| 867 | return input.error("Improperly nested 'key-set' tag at " |
| 868 | + parser.getPositionDescription()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 869 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 870 | 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)) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 910 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 911 | /* public-key first definition, or matches old definition */ |
| 912 | publicKeys.put(publicKeyName, currentKey); |
| 913 | } else { |
| 914 | return input.error("Value of 'public-key' " + publicKeyName |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 915 | + " conflicts with previously defined value at " |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 916 | + parser.getPositionDescription()); |
| 917 | } |
| 918 | } |
| 919 | definedKeySets.get(currentKeySet).add(publicKeyName); |
| 920 | XmlUtils.skipCurrentTag(parser); |
| 921 | } finally { |
| 922 | sa.recycle(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 923 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 924 | } 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 944 | } |
| 945 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 946 | String packageName = pkg.getPackageName(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 947 | Set<String> publicKeyNames = publicKeys.keySet(); |
| 948 | if (publicKeyNames.removeAll(definedKeySets.keySet())) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 949 | return input.error("Package" + packageName |
| 950 | + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 951 | } |
| 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()) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 968 | pkg.addKeySet(keySetName, publicKeys.get(s)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 969 | } |
| 970 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 971 | if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) { |
| 972 | pkg.setUpgradeKeySets(upgradeKeySets); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 973 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 974 | return input.error("Package" + packageName |
| 975 | + " AndroidManifest.xml does not define all 'upgrade-key-set's ."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 976 | } |
| 977 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 978 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 979 | } |
| 980 | |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame] | 981 | 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); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 986 | if (result.isError()) { |
| 987 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 988 | } |
Philip P. Moltmann | 12ac3f4 | 2020-03-05 15:01:29 -0800 | [diff] [blame] | 989 | return input.success(pkg.addAttribution(result.getResult())); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 990 | } |
| 991 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 992 | private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input, |
| 993 | ParsingPackage pkg, Resources res, XmlResourceParser parser) |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 994 | throws XmlPullParserException, IOException { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 995 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1002 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1003 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1013 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1014 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1042 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1043 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1044 | final String requiredFeature = sa.getNonConfigurationString( |
| 1045 | R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1046 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1047 | final String requiredNotfeature = sa.getNonConfigurationString( |
| 1048 | R.styleable.AndroidManifestUsesPermission_requiredNotFeature, |
| 1049 | 0); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1050 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1051 | XmlUtils.skipCurrentTag(parser); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1052 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1053 | // Can only succeed from here on out |
| 1054 | ParseResult<ParsingPackage> success = input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1055 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1056 | if (name == null) { |
| 1057 | return success; |
| 1058 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1059 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1060 | 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 | |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1085 | return success; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1086 | } finally { |
| 1087 | sa.recycle(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1088 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1089 | } |
| 1090 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1091 | private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input, |
| 1092 | ParsingPackage pkg, Resources res, XmlResourceParser parser) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1093 | ConfigurationInfo cPref = new ConfigurationInfo(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1094 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1119 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1120 | } |
| 1121 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1122 | private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input, |
| 1123 | ParsingPackage pkg, Resources res, XmlResourceParser parser) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1124 | FeatureInfo fi = parseFeatureInfo(res, parser); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1125 | pkg.addReqFeature(fi); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1126 | |
| 1127 | if (fi.name == null) { |
| 1128 | ConfigurationInfo cPref = new ConfigurationInfo(); |
| 1129 | cPref.reqGlEsVersion = fi.reqGlEsVersion; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1130 | pkg.addConfigPreference(cPref); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1131 | } |
| 1132 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1133 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1134 | } |
| 1135 | |
| 1136 | private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) { |
| 1137 | FeatureInfo fi = new FeatureInfo(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1138 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1154 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1155 | } |
| 1156 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1157 | private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input, |
| 1158 | ParsingPackage pkg, Resources res, XmlResourceParser parser) |
| 1159 | throws IOException, XmlPullParserException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1160 | FeatureGroupInfo group = new FeatureGroupInfo(); |
| 1161 | ArrayList<FeatureInfo> features = null; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1162 | final int depth = parser.getDepth(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1163 | int type; |
| 1164 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
| 1165 | && (type != XmlPullParser.END_TAG |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1166 | || parser.getDepth() > depth)) { |
| 1167 | if (type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1168 | 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 + |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1181 | " at " + pkg.getBaseCodePath() + " " + |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1182 | parser.getPositionDescription()); |
| 1183 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1184 | } |
| 1185 | |
| 1186 | if (features != null) { |
| 1187 | group.features = new FeatureInfo[features.size()]; |
| 1188 | group.features = features.toArray(group.features); |
| 1189 | } |
| 1190 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1191 | pkg.addFeatureGroup(group); |
| 1192 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1193 | } |
| 1194 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1195 | private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input, |
| 1196 | ParsingPackage pkg, Resources res, XmlResourceParser parser) |
| 1197 | throws IOException, XmlPullParserException { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1198 | if (PackageParser.SDK_VERSION > 0) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1199 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1205 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1206 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1214 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1215 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1216 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1226 | } |
| 1227 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1228 | targetVers = minVers; |
| 1229 | targetCode = minCode; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1230 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1231 | |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 1232 | 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1246 | ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode, |
| 1247 | PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input); |
| 1248 | if (minSdkVersionResult.isError()) { |
| 1249 | return input.error(minSdkVersionResult); |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1250 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1251 | |
| 1252 | int minSdkVersion = minSdkVersionResult.getResult(); |
| 1253 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1254 | pkg.setMinSdkVersion(minSdkVersion) |
| 1255 | .setTargetSdkVersion(targetSdkVersion); |
| 1256 | |
| 1257 | int type; |
| 1258 | final int innerDepth = parser.getDepth(); |
Oli Lan | 72ae447 | 2020-04-14 16:50:41 +0100 | [diff] [blame] | 1259 | SparseIntArray minExtensionVersions = null; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1260 | 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 Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1264 | } |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1265 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1266 | final ParseResult result; |
| 1267 | if (parser.getName().equals("extension-sdk")) { |
Oli Lan | 72ae447 | 2020-04-14 16:50:41 +0100 | [diff] [blame] | 1268 | if (minExtensionVersions == null) { |
| 1269 | minExtensionVersions = new SparseIntArray(); |
| 1270 | } |
| 1271 | result = parseExtensionSdk(input, res, parser, minExtensionVersions); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1272 | 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 Lan | 72ae447 | 2020-04-14 16:50:41 +0100 | [diff] [blame] | 1281 | pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions)); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1282 | } finally { |
| 1283 | sa.recycle(); |
| 1284 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1285 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1286 | return input.success(pkg); |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1287 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1288 | |
Oli Lan | 72ae447 | 2020-04-14 16:50:41 +0100 | [diff] [blame] | 1289 | @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) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1304 | 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 Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1313 | |
| 1314 | if (sdkVersion < 0) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1315 | return input.error( |
| 1316 | PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, |
| 1317 | "<extension-sdk> must specify an sdkVersion >= 0"); |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1318 | } |
| 1319 | if (minVersion < 0) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1320 | return input.error( |
| 1321 | PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, |
| 1322 | "<extension-sdk> must specify minExtensionVersion >= 0"); |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1323 | } |
| 1324 | |
| 1325 | try { |
| 1326 | int version = SdkExtensions.getExtensionVersion(sdkVersion); |
| 1327 | if (version < minVersion) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1328 | return input.error( |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1329 | PackageManager.INSTALL_FAILED_OLDER_SDK, |
| 1330 | "Package requires " + sdkVersion + " extension version " + minVersion |
| 1331 | + " which exceeds device version " + version); |
| 1332 | } |
| 1333 | } catch (RuntimeException e) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1334 | return input.error( |
Anton Hansson | b2f709d | 2020-01-09 10:25:23 +0000 | [diff] [blame] | 1335 | PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, |
| 1336 | "Specified sdkVersion " + sdkVersion + " is not valid"); |
| 1337 | } |
Oli Lan | 72ae447 | 2020-04-14 16:50:41 +0100 | [diff] [blame] | 1338 | minExtensionVersions.put(sdkVersion, minVersion); |
| 1339 | return input.success(minExtensionVersions); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1340 | } |
| 1341 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1342 | /** |
| 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1353 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1354 | |
| 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 + ")"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1359 | } |
| 1360 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1361 | // 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1378 | } |
| 1379 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1380 | /** |
| 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1391 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1392 | // 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1459 | int type; |
| 1460 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
| 1461 | && (type != XmlPullParser.END_TAG |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1462 | || parser.getDepth() > depth)) { |
| 1463 | if (type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1464 | continue; |
| 1465 | } |
| 1466 | if (parser.getName().equals("intent")) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1467 | ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null, |
| 1468 | pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input); |
| 1469 | if (result.isError()) { |
| 1470 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1471 | } |
| 1472 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1473 | ParsedIntentInfo intentInfo = result.getResult(); |
| 1474 | |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1475 | Uri data = null; |
| 1476 | String dataType = null; |
Patrick Baumann | 28a6810 | 2020-03-17 10:40:49 -0700 | [diff] [blame] | 1477 | String host = IntentFilter.WILDCARD; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1478 | 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)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1483 | return input.error("intent tags must contain either an action or data."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1484 | } |
| 1485 | if (numActions > 1) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1486 | return input.error("intent tag may have at most one action."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1487 | } |
| 1488 | if (numTypes > 1) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1489 | return input.error("intent tag may have at most one data type."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1490 | } |
| 1491 | if (numSchemes > 1) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1492 | return input.error("intent tag may have at most one data scheme."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1493 | } |
| 1494 | if (numHosts > 1) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1495 | return input.error("intent tag may have at most one data host."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1496 | } |
| 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 Baumann | 28a6810 | 2020-03-17 10:40:49 -0700 | [diff] [blame] | 1508 | .path(IntentFilter.WILDCARD_PATH) |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1509 | .build(); |
| 1510 | } |
| 1511 | if (numTypes == 1) { |
| 1512 | dataType = intentInfo.getDataType(0); |
Patrick Baumann | 28a6810 | 2020-03-17 10:40:49 -0700 | [diff] [blame] | 1513 | // 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1525 | } |
| 1526 | intent.setDataAndType(data, dataType); |
| 1527 | if (numActions == 1) { |
| 1528 | intent.setAction(intentInfo.getAction(0)); |
| 1529 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1530 | pkg.addQueriesIntent(intent); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1531 | } else if (parser.getName().equals("package")) { |
| 1532 | final TypedArray sa = res.obtainAttributes(parser, |
| 1533 | R.styleable.AndroidManifestQueriesPackage); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1534 | final String packageName = sa.getString( |
| 1535 | R.styleable.AndroidManifestQueriesPackage_name); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1536 | if (TextUtils.isEmpty(packageName)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1537 | return input.error("Package name is missing from package tag."); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1538 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1539 | pkg.addQueriesPackage(packageName.intern()); |
Patrick Baumann | 9918123 | 2020-01-28 10:55:25 -0800 | [diff] [blame] | 1540 | } 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)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1547 | return input.error( |
Patrick Baumann | 9918123 | 2020-01-28 10:55:25 -0800 | [diff] [blame] | 1548 | PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, |
| 1549 | "Authority missing from provider tag." |
| 1550 | ); |
| 1551 | } |
| 1552 | StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";"); |
| 1553 | while (authoritiesTokenizer.hasMoreElements()) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1554 | pkg.addQueriesProvider(authoritiesTokenizer.nextToken()); |
Patrick Baumann | 9918123 | 2020-01-28 10:55:25 -0800 | [diff] [blame] | 1555 | } |
| 1556 | } finally { |
| 1557 | sa.recycle(); |
| 1558 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1559 | } |
| 1560 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1561 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1562 | } |
| 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 | * |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1571 | * 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. |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1574 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1575 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1580 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1581 | TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1582 | try { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1583 | // 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"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1588 | } |
| 1589 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1590 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1599 | } |
| 1600 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1601 | pkg.setClassName(outInfoName); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1602 | } |
| 1603 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1604 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1611 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1612 | 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()) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1628 | // backupAgent, killAfterRestore, fullBackupContent, backupInForeground, |
| 1629 | // and restoreAnyVersion are only relevant if backup is possible for the |
| 1630 | // given application. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1631 | String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION, |
| 1632 | R.styleable.AndroidManifestApplication_backupAgent, sa); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1633 | if (backupAgent != null) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1634 | String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1635 | if (backupAgentName == null) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1636 | return input.error("Empty class name in package " + pkgName); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1637 | } |
| 1638 | |
| 1639 | if (PackageParser.DEBUG_BACKUP) { |
| 1640 | Slog.v(TAG, "android:backupAgent = " + backupAgentName |
| 1641 | + " from " + pkgName + "+" + backupAgent); |
| 1642 | } |
| 1643 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1644 | 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)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1653 | } |
| 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1671 | pkg.setFullBackupContent(fullBackupContent); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1672 | } |
| 1673 | if (PackageParser.DEBUG_BACKUP) { |
| 1674 | Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName); |
| 1675 | } |
| 1676 | } |
| 1677 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1678 | if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1679 | // Check if persistence is based on a feature being present |
| 1680 | final String requiredFeature = sa.getNonResourceString(R.styleable |
| 1681 | .AndroidManifestApplication_persistentWhenFeatureAvailable); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1682 | pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1683 | } |
| 1684 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1685 | // 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()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1689 | |
| 1690 | if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1691 | pkg.setResizeableActivity(sa.getBoolean( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1692 | R.styleable.AndroidManifestApplication_resizeableActivity, true)); |
| 1693 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1694 | pkg.setResizeableActivityViaSdkVersion( |
| 1695 | targetSdk >= Build.VERSION_CODES.N); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1696 | } |
| 1697 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1698 | String taskAffinity; |
| 1699 | if (targetSdk >= Build.VERSION_CODES.FROYO) { |
| 1700 | taskAffinity = sa.getNonConfigurationString( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1701 | 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. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1707 | taskAffinity = sa.getNonResourceString( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1708 | R.styleable.AndroidManifestApplication_taskAffinity); |
| 1709 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1710 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1711 | ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName( |
| 1712 | pkgName, pkgName, taskAffinity, input); |
| 1713 | if (taskAffinityResult.isError()) { |
| 1714 | return input.error(taskAffinityResult); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1715 | } |
| 1716 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1717 | pkg.setTaskAffinity(taskAffinityResult.getResult()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1718 | String factory = sa.getNonResourceString( |
| 1719 | R.styleable.AndroidManifestApplication_appComponentFactory); |
| 1720 | if (factory != null) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1721 | String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1722 | if (appComponentFactory == null) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1723 | return input.error("Empty class name in package " + pkgName); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1724 | } |
| 1725 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1726 | pkg.setAppComponentFactory(appComponentFactory); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1727 | } |
| 1728 | |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1729 | CharSequence pname; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1730 | if (targetSdk >= Build.VERSION_CODES.FROYO) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1731 | 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 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1741 | ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName( |
| 1742 | pkgName, null, pname, flags, mSeparateProcesses, input); |
| 1743 | if (processNameResult.isError()) { |
| 1744 | return input.error(processNameResult); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1745 | } |
| 1746 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1747 | String processName = processNameResult.getResult(); |
| 1748 | pkg.setProcessName(processName); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1749 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1750 | if (pkg.isCantSaveState()) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1751 | // A heavy-weight application can not be in a custom process. |
| 1752 | // We can do direct compare because we intern all strings. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1753 | if (processName != null && !processName.equals(pkgName)) { |
| 1754 | return input.error( |
| 1755 | "cantSaveState applications can not use custom processes"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1756 | } |
| 1757 | } |
| 1758 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1759 | String classLoaderName = pkg.getClassLoaderName(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1760 | if (classLoaderName != null |
| 1761 | && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1762 | return input.error("Invalid class loader name: " + classLoaderName); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1763 | } |
Evgenii Stepanov | 102d3d8 | 2020-02-12 16:48:14 -0800 | [diff] [blame] | 1764 | |
Evgenii Stepanov | d43d109 | 2020-03-16 13:55:42 -0700 | [diff] [blame] | 1765 | pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1766 | } finally { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1767 | sa.recycle(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1768 | } |
| 1769 | |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1770 | boolean hasActivityOrder = false; |
| 1771 | boolean hasReceiverOrder = false; |
| 1772 | boolean hasServiceOrder = false; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1773 | final int depth = parser.getDepth(); |
| 1774 | int type; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1775 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1776 | && (type != XmlPullParser.END_TAG |
| 1777 | || parser.getDepth() > depth)) { |
| 1778 | if (type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1779 | continue; |
| 1780 | } |
| 1781 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1782 | final ParseResult result; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1783 | String tagName = parser.getName(); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1784 | boolean isActivity = false; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1785 | switch (tagName) { |
| 1786 | case "activity": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1787 | isActivity = true; |
| 1788 | // fall-through |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1789 | case "receiver": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1790 | 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1803 | } |
| 1804 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1805 | result = activityResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1806 | break; |
| 1807 | case "service": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1808 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1815 | } |
| 1816 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1817 | result = serviceResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1818 | break; |
| 1819 | case "provider": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1820 | ParseResult<ParsedProvider> providerResult = |
| 1821 | ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser, |
| 1822 | flags, PackageParser.sUseRoundIcon, input); |
| 1823 | if (providerResult.isSuccess()) { |
| 1824 | pkg.addProvider(providerResult.getResult()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1825 | } |
| 1826 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1827 | result = providerResult; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1828 | break; |
| 1829 | case "activity-alias": |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1830 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1836 | } |
| 1837 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1838 | result = activityResult; |
Winson | 7ea528a | 2019-12-13 14:41:40 -0800 | [diff] [blame] | 1839 | break; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1840 | default: |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1841 | result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags); |
| 1842 | break; |
| 1843 | } |
| 1844 | |
| 1845 | if (result.isError()) { |
| 1846 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1847 | } |
| 1848 | } |
| 1849 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1850 | if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1851 | // Add a hidden app detail activity to normal apps which forwards user to App Details |
| 1852 | // page. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1853 | ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg); |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 1854 | 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1862 | pkg.addActivity(a.getResult()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1863 | } |
| 1864 | |
| 1865 | if (hasActivityOrder) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1866 | pkg.sortActivities(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1867 | } |
| 1868 | if (hasReceiverOrder) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1869 | pkg.sortReceivers(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1870 | } |
| 1871 | if (hasServiceOrder) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1872 | pkg.sortServices(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1873 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1874 | |
| 1875 | // Must be run after the entire {@link ApplicationInfo} has been fully processed and after |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1876 | // every activity info has had a chance to set it from its attributes. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1877 | setMaxAspectRatio(pkg); |
| 1878 | setMinAspectRatio(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1879 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1880 | pkg.setHasDomainUrls(hasDomainURLs(pkg)); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1881 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1882 | return input.success(pkg); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1883 | } |
| 1884 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1885 | /** |
| 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)) |
Winson | a20ba0b | 2020-03-03 13:17:36 -0800 | [diff] [blame] | 1907 | .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa)) |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1908 | .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 Susla | 49b84c3 | 2020-03-23 15:19:29 -0700 | [diff] [blame] | 1923 | .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa)) |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1924 | // 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1954 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1955 | /** |
| 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 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1974 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1975 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1994 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1995 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 1996 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 1997 | @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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2012 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2013 | // 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" |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2021 | ); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2022 | } else if (pkg.getStaticSharedLibName() != null) { |
| 2023 | return input.error("Multiple static-shared libs for package " |
| 2024 | + pkg.getPackageName()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2025 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2026 | |
| 2027 | return input.success(pkg.setStaticSharedLibName(lname.intern()) |
| 2028 | .setStaticSharedLibVersion( |
| 2029 | PackageInfo.composeLongVersionCode(versionMajor, version)) |
| 2030 | .setStaticSharedLibrary(true)); |
| 2031 | } finally { |
| 2032 | sa.recycle(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2033 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2034 | } |
| 2035 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2036 | @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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2045 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2046 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2175 | int type; |
| 2176 | while ((type = parser.next()) != XmlPullParser.END_DOCUMENT |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2177 | && (type != XmlPullParser.END_TAG |
| 2178 | || parser.getDepth() > depth)) { |
| 2179 | if (type != XmlPullParser.START_TAG) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2180 | continue; |
| 2181 | } |
| 2182 | |
| 2183 | final String nodeName = parser.getName(); |
| 2184 | if (nodeName.equals("additional-certificate")) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2185 | TypedArray sa = resources.obtainAttributes(parser, |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2186 | R.styleable.AndroidManifestAdditionalCertificate); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2187 | try { |
| 2188 | String certSha256Digest = sa.getNonResourceString( |
| 2189 | R.styleable.AndroidManifestAdditionalCertificate_certDigest); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2190 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2191 | 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 { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2203 | sa.recycle(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2204 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2205 | } |
| 2206 | } |
| 2207 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2208 | return input.success(certSha256Digests); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2209 | } |
| 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. |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2214 | */ |
| 2215 | @NonNull |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2216 | private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input, |
| 2217 | ParsingPackage pkg) { |
| 2218 | String packageName = pkg.getPackageName(); |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 2219 | ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName( |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2220 | packageName, packageName, ":app_details", input); |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 2221 | if (result.isError()) { |
| 2222 | return input.error(result); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2223 | } |
| 2224 | |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 2225 | String taskAffinity = result.getResult(); |
| 2226 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2227 | // 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())); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2231 | } |
| 2232 | |
| 2233 | /** |
| 2234 | * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI |
| 2235 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2236 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2245 | 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 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2260 | private static void setMaxAspectRatio(ParsingPackage pkg) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2261 | // 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. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2263 | float maxAspectRatio = pkg.getTargetSdkVersion() < O |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2264 | ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; |
| 2265 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2266 | float packageMaxAspectRatio = pkg.getMaxAspectRatio(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2267 | if (packageMaxAspectRatio != 0) { |
| 2268 | // Use the application max aspect ration as default if set. |
| 2269 | maxAspectRatio = packageMaxAspectRatio; |
| 2270 | } else { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2271 | Bundle appMetaData = pkg.getMetaData(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2272 | 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2279 | 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; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2286 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2287 | |
| 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2299 | } |
| 2300 | } |
| 2301 | |
| 2302 | /** |
| 2303 | * Sets the min aspect ratio of every child activity that doesn't already have an aspect |
| 2304 | * ratio set. |
| 2305 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2306 | private void setMinAspectRatio(ParsingPackage pkg) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2307 | final float minAspectRatio; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2308 | float packageMinAspectRatio = pkg.getMinAspectRatio(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2309 | 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. |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2316 | minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2317 | ? 0 |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2318 | : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH)) |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2319 | ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH |
| 2320 | : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO; |
| 2321 | } |
| 2322 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2323 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2329 | } |
| 2330 | } |
| 2331 | } |
| 2332 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2333 | private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg, |
| 2334 | Resources res, XmlResourceParser parser) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2335 | TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2336 | try { |
| 2337 | String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage); |
| 2338 | int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2339 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2340 | 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"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2344 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2345 | |
| 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2373 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2374 | } |
| 2375 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2376 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2389 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2390 | } |
| 2391 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2392 | 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) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2471 | 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]; |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2476 | if (pkg.getTargetSdkVersion() >= npi.sdkVersion) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2477 | break; |
| 2478 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2479 | if (!pkg.getRequestedPermissions().contains(npi.name)) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2480 | if (newPermsMsg == null) { |
| 2481 | newPermsMsg = new StringBuilder(128); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2482 | newPermsMsg.append(pkg.getPackageName()); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2483 | newPermsMsg.append(": compat added "); |
| 2484 | } else { |
| 2485 | newPermsMsg.append(' '); |
| 2486 | } |
| 2487 | newPermsMsg.append(npi.name); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2488 | pkg.addRequestedPermission(npi.name) |
| 2489 | .addImplicitPermission(npi.name); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2490 | } |
| 2491 | } |
| 2492 | if (newPermsMsg != null) { |
| 2493 | Slog.i(TAG, newPermsMsg.toString()); |
| 2494 | } |
| 2495 | } |
| 2496 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2497 | private static void convertSplitPermissions(ParsingPackage pkg) { |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2498 | 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); |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2509 | List<String> requestedPermissions = pkg.getRequestedPermissions(); |
| 2510 | if (pkg.getTargetSdkVersion() >= spi.getTargetSdk() |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2511 | || !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)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2518 | pkg.addRequestedPermission(perm) |
| 2519 | .addImplicitPermission(perm); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2520 | } |
| 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 | * |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2547 | * @param pkg The package which needs to be marked as unresizable. |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2548 | */ |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2549 | 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2556 | } |
| 2557 | } |
| 2558 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2559 | private static ParseResult validateName(ParseInput input, String name, boolean requireSeparator, |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2560 | 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 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2580 | return input.error("bad character '" + c + "'"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2581 | } |
| 2582 | if (requireFilename && !FileUtils.isValidExtFilename(name)) { |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2583 | return input.error("Invalid filename"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2584 | } |
| 2585 | return hasSep || !requireSeparator |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2586 | ? input.success(null) |
| 2587 | : input.error("must have at least one '.' separator"); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2588 | } |
| 2589 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2590 | 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2596 | } |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2597 | |
| 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(); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2643 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2644 | } |
| 2645 | |
| 2646 | /** |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2647 | * Collect certificates from all the APKs described in the given package. Also asserts that |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2648 | * all APK contents are signed correctly and consistently. |
| 2649 | */ |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2650 | public static SigningDetails collectCertificates(ParsingPackageRead pkg, boolean skipVerify) |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2651 | throws PackageParserException { |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2652 | SigningDetails signingDetails = SigningDetails.UNKNOWN; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2653 | |
| 2654 | Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); |
| 2655 | try { |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2656 | signingDetails = collectCertificates( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2657 | pkg.getBaseCodePath(), |
| 2658 | skipVerify, |
| 2659 | pkg.isStaticSharedLibrary(), |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2660 | signingDetails, |
Michael Groover | 33df7c4 | 2020-01-24 19:00:01 -0800 | [diff] [blame] | 2661 | pkg.getTargetSdkVersion() |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2662 | ); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2663 | |
| 2664 | String[] splitCodePaths = pkg.getSplitCodePaths(); |
| 2665 | if (!ArrayUtils.isEmpty(splitCodePaths)) { |
| 2666 | for (int i = 0; i < splitCodePaths.length; i++) { |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2667 | signingDetails = collectCertificates( |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2668 | splitCodePaths[i], |
| 2669 | skipVerify, |
| 2670 | pkg.isStaticSharedLibrary(), |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2671 | signingDetails, |
Michael Groover | 33df7c4 | 2020-01-24 19:00:01 -0800 | [diff] [blame] | 2672 | pkg.getTargetSdkVersion() |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2673 | ); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2674 | } |
| 2675 | } |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2676 | return signingDetails; |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2677 | } finally { |
| 2678 | Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); |
| 2679 | } |
| 2680 | } |
| 2681 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2682 | public static SigningDetails collectCertificates(String baseCodePath, boolean skipVerify, |
| 2683 | boolean isStaticSharedLibrary, @NonNull SigningDetails existingSigningDetails, |
| 2684 | int targetSdk) throws PackageParserException { |
Michael Groover | 33df7c4 | 2020-01-24 19:00:01 -0800 | [diff] [blame] | 2685 | int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk( |
| 2686 | targetSdk); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2687 | 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 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2716 | /* |
| 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); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2734 | } |
| 2735 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2736 | private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) { |
| 2737 | return sa.getFloat(attribute, defaultValue); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2738 | } |
| 2739 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2740 | private static float aFloat(@StyleableRes int attribute, TypedArray sa) { |
| 2741 | return sa.getFloat(attribute, 0f); |
| 2742 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2743 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2744 | private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) { |
| 2745 | return sa.getInt(attribute, defaultValue); |
| 2746 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2747 | |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 2748 | private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) { |
| 2749 | return sa.getInteger(attribute, defaultValue); |
| 2750 | } |
| 2751 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2752 | private static int anInt(@StyleableRes int attribute, TypedArray sa) { |
| 2753 | return sa.getInt(attribute, 0); |
| 2754 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2755 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2756 | @AnyRes |
| 2757 | private static int resId(@StyleableRes int attribute, TypedArray sa) { |
| 2758 | return sa.getResourceId(attribute, 0); |
| 2759 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2760 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2761 | private static String string(@StyleableRes int attribute, TypedArray sa) { |
| 2762 | return sa.getString(attribute); |
| 2763 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2764 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2765 | private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute, |
| 2766 | TypedArray sa) { |
| 2767 | return sa.getNonConfigurationString(attribute, allowedChangingConfigs); |
| 2768 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2769 | |
Winson | f00c755 | 2020-01-28 12:52:01 -0800 | [diff] [blame] | 2770 | private static String nonResString(@StyleableRes int index, TypedArray sa) { |
| 2771 | return sa.getNonResourceString(index); |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2772 | } |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2773 | |
| 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 | |
Winson | 727da64 | 2020-03-10 15:25:32 -0700 | [diff] [blame] | 2781 | ParsingPackage startParsingPackage(@NonNull String packageName, |
| 2782 | @NonNull String baseCodePath, @NonNull String codePath, |
Todd Kennedy | 83eddae | 2020-03-02 09:21:25 -0800 | [diff] [blame] | 2783 | @NonNull TypedArray manifestArray, boolean isCoreApp); |
Winson | e23ae20 | 2020-01-24 11:56:44 -0800 | [diff] [blame] | 2784 | } |
Winson | 14ff717 | 2019-10-23 10:42:27 -0700 | [diff] [blame] | 2785 | } |