All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.content.pm.PackageParser Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 14-robolectric-10818077
Show newest version
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.content.pm;

import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructStat;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
import android.util.apk.ApkSignatureSchemeV2Verifier;
import android.util.jar.StrictJarFile;
import android.view.Gravity;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;

import libcore.io.IoUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;

/**
 * Parser for package files (APKs) on disk. This supports apps packaged either
 * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple
 * APKs in a single directory.
 * 

* Apps packaged as multiple APKs always consist of a single "base" APK (with a * {@code null} split name) and zero or more "split" APKs (with unique split * names). Any subset of those split APKs are a valid install, as long as the * following constraints are met: *

    *
  • All APKs must have the exact same package name, version code, and signing * certificates. *
  • All APKs must have unique split names. *
  • All installations must contain a single base APK. *
* * @hide */ public class PackageParser { private static final boolean DEBUG_JAR = false; private static final boolean DEBUG_PARSER = false; private static final boolean DEBUG_BACKUP = false; private static final String PROPERTY_CHILD_PACKAGES_ENABLED = "persist.sys.child_packages_enabled"; private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false); private static final int MAX_PACKAGES_PER_APK = 5; public static final int APK_SIGNING_UNKNOWN = 0; public static final int APK_SIGNING_V1 = 1; public static final int APK_SIGNING_V2 = 2; private static final float DEFAULT_PRE_O_MAX_ASPECT_RATIO = 1.86f; // TODO: switch outError users to PackageParserException // TODO: refactor "codePath" to "apkPath" /** File name in an APK for the Android manifest. */ private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml"; /** Path prefix for apps on expanded storage */ private static final String MNT_EXPAND = "/mnt/expand/"; private static final String TAG_MANIFEST = "manifest"; private static final String TAG_APPLICATION = "application"; private static final String TAG_PACKAGE_VERIFIER = "package-verifier"; private static final String TAG_OVERLAY = "overlay"; private static final String TAG_KEY_SETS = "key-sets"; private static final String TAG_PERMISSION_GROUP = "permission-group"; private static final String TAG_PERMISSION = "permission"; private static final String TAG_PERMISSION_TREE = "permission-tree"; private static final String TAG_USES_PERMISSION = "uses-permission"; private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m"; private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23"; private static final String TAG_USES_CONFIGURATION = "uses-configuration"; private static final String TAG_USES_FEATURE = "uses-feature"; private static final String TAG_FEATURE_GROUP = "feature-group"; private static final String TAG_USES_SDK = "uses-sdk"; private static final String TAG_SUPPORT_SCREENS = "supports-screens"; private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast"; private static final String TAG_INSTRUMENTATION = "instrumentation"; private static final String TAG_ORIGINAL_PACKAGE = "original-package"; private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions"; private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens"; private static final String TAG_SUPPORTS_INPUT = "supports-input"; private static final String TAG_EAT_COMMENT = "eat-comment"; private static final String TAG_PACKAGE = "package"; private static final String TAG_RESTRICT_UPDATE = "restrict-update"; private static final String TAG_USES_SPLIT = "uses-split"; // [b/36551762] STOPSHIP remove the ability to expose components via meta-data // Temporary workaround; allow meta-data to expose components to instant apps private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed"; /** * Bit mask of all the valid bits that can be set in recreateOnConfigChanges. * @hide */ private static final int RECREATE_ON_CONFIG_CHANGES_MASK = ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC; // These are the tags supported by child packages private static final Set CHILD_PACKAGE_TAGS = new ArraySet<>(); static { CHILD_PACKAGE_TAGS.add(TAG_APPLICATION); CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION); CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_M); CHILD_PACKAGE_TAGS.add(TAG_USES_PERMISSION_SDK_23); CHILD_PACKAGE_TAGS.add(TAG_USES_CONFIGURATION); CHILD_PACKAGE_TAGS.add(TAG_USES_FEATURE); CHILD_PACKAGE_TAGS.add(TAG_FEATURE_GROUP); CHILD_PACKAGE_TAGS.add(TAG_USES_SDK); CHILD_PACKAGE_TAGS.add(TAG_SUPPORT_SCREENS); CHILD_PACKAGE_TAGS.add(TAG_INSTRUMENTATION); CHILD_PACKAGE_TAGS.add(TAG_USES_GL_TEXTURE); CHILD_PACKAGE_TAGS.add(TAG_COMPATIBLE_SCREENS); CHILD_PACKAGE_TAGS.add(TAG_SUPPORTS_INPUT); CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT); } private static final boolean LOG_UNSAFE_BROADCASTS = false; // Set of broadcast actions that are safe for manifest receivers private static final Set SAFE_BROADCASTS = new ArraySet<>(); static { SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED); } /** @hide */ public static class NewPermissionInfo { public final String name; public final int sdkVersion; public final int fileVersion; public NewPermissionInfo(String name, int sdkVersion, int fileVersion) { this.name = name; this.sdkVersion = sdkVersion; this.fileVersion = fileVersion; } } /** @hide */ public static class SplitPermissionInfo { public final String rootPerm; public final String[] newPerms; public final int targetSdk; public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) { this.rootPerm = rootPerm; this.newPerms = newPerms; this.targetSdk = targetSdk; } } /** * List of new permissions that have been added since 1.0. * NOTE: These must be declared in SDK version order, with permissions * added to older SDKs appearing before those added to newer SDKs. * If sdkVersion is 0, then this is not a permission that we want to * automatically add to older apps, but we do want to allow it to be * granted during a platform update. * @hide */ public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] = new PackageParser.NewPermissionInfo[] { new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.os.Build.VERSION_CODES.DONUT, 0), new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE, android.os.Build.VERSION_CODES.DONUT, 0) }; /** * List of permissions that have been split into more granular or dependent * permissions. * @hide */ public static final PackageParser.SplitPermissionInfo SPLIT_PERMISSIONS[] = new PackageParser.SplitPermissionInfo[] { // READ_EXTERNAL_STORAGE is always required when an app requests // WRITE_EXTERNAL_STORAGE, because we can't have an app that has // write access without read access. The hack here with the target // target SDK version ensures that this grant is always done. new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE }, android.os.Build.VERSION_CODES.CUR_DEVELOPMENT+1), new PackageParser.SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS, new String[] { android.Manifest.permission.READ_CALL_LOG }, android.os.Build.VERSION_CODES.JELLY_BEAN), new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS, new String[] { android.Manifest.permission.WRITE_CALL_LOG }, android.os.Build.VERSION_CODES.JELLY_BEAN) }; /** * @deprecated callers should move to explicitly passing around source path. */ @Deprecated private String mArchiveSourcePath; private String[] mSeparateProcesses; private boolean mOnlyCoreApps; private DisplayMetrics mMetrics; private Callback mCallback; private File mCacheDir; private static final int SDK_VERSION = Build.VERSION.SDK_INT; private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; private int mParseError = PackageManager.INSTALL_SUCCEEDED; private static boolean sCompatibilityModeEnabled = true; private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; private static final int PARSE_DEFAULT_TARGET_SANDBOX = 1; static class ParsePackageItemArgs { final Package owner; final String[] outError; final int nameRes; final int labelRes; final int iconRes; final int roundIconRes; final int logoRes; final int bannerRes; String tag; TypedArray sa; ParsePackageItemArgs(Package _owner, String[] _outError, int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes, int _bannerRes) { owner = _owner; outError = _outError; nameRes = _nameRes; labelRes = _labelRes; iconRes = _iconRes; logoRes = _logoRes; bannerRes = _bannerRes; roundIconRes = _roundIconRes; } } /** @hide */ @VisibleForTesting public static class ParseComponentArgs extends ParsePackageItemArgs { final String[] sepProcesses; final int processRes; final int descriptionRes; final int enabledRes; int flags; public ParseComponentArgs(Package _owner, String[] _outError, int _nameRes, int _labelRes, int _iconRes, int _roundIconRes, int _logoRes, int _bannerRes, String[] _sepProcesses, int _processRes, int _descriptionRes, int _enabledRes) { super(_owner, _outError, _nameRes, _labelRes, _iconRes, _roundIconRes, _logoRes, _bannerRes); sepProcesses = _sepProcesses; processRes = _processRes; descriptionRes = _descriptionRes; enabledRes = _enabledRes; } } /** * Lightweight parsed details about a single package. */ public static class PackageLite { public final String packageName; public final int versionCode; public final int installLocation; public final VerifierInfo[] verifiers; /** Names of any split APKs, ordered by parsed splitName */ public final String[] splitNames; /** Names of any split APKs that are features. Ordered by splitName */ public final boolean[] isFeatureSplits; /** Dependencies of any split APKs, ordered by parsed splitName */ public final String[] usesSplitNames; public final String[] configForSplit; /** * Path where this package was found on disk. For monolithic packages * this is path to single base APK file; for cluster packages this is * path to the cluster directory. */ public final String codePath; /** Path of base APK */ public final String baseCodePath; /** Paths of any split APKs, ordered by parsed splitName */ public final String[] splitCodePaths; /** Revision code of base APK */ public final int baseRevisionCode; /** Revision codes of any split APKs, ordered by parsed splitName */ public final int[] splitRevisionCodes; public final boolean coreApp; public final boolean debuggable; public final boolean multiArch; public final boolean use32bitAbi; public final boolean extractNativeLibs; public final boolean isolatedSplits; public PackageLite(String codePath, ApkLite baseApk, String[] splitNames, boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit, String[] splitCodePaths, int[] splitRevisionCodes) { this.packageName = baseApk.packageName; this.versionCode = baseApk.versionCode; this.installLocation = baseApk.installLocation; this.verifiers = baseApk.verifiers; this.splitNames = splitNames; this.isFeatureSplits = isFeatureSplits; this.usesSplitNames = usesSplitNames; this.configForSplit = configForSplit; this.codePath = codePath; this.baseCodePath = baseApk.codePath; this.splitCodePaths = splitCodePaths; this.baseRevisionCode = baseApk.revisionCode; this.splitRevisionCodes = splitRevisionCodes; this.coreApp = baseApk.coreApp; this.debuggable = baseApk.debuggable; this.multiArch = baseApk.multiArch; this.use32bitAbi = baseApk.use32bitAbi; this.extractNativeLibs = baseApk.extractNativeLibs; this.isolatedSplits = baseApk.isolatedSplits; } public List getAllCodePaths() { ArrayList paths = new ArrayList<>(); paths.add(baseCodePath); if (!ArrayUtils.isEmpty(splitCodePaths)) { Collections.addAll(paths, splitCodePaths); } return paths; } } /** * Lightweight parsed details about a single APK file. */ public static class ApkLite { public final String codePath; public final String packageName; public final String splitName; public boolean isFeatureSplit; public final String configForSplit; public final String usesSplitName; public final int versionCode; public final int revisionCode; public final int installLocation; public final VerifierInfo[] verifiers; public final Signature[] signatures; public final Certificate[][] certificates; public final boolean coreApp; public final boolean debuggable; public final boolean multiArch; public final boolean use32bitAbi; public final boolean extractNativeLibs; public final boolean isolatedSplits; public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit, String configForSplit, String usesSplitName, int versionCode, int revisionCode, int installLocation, List verifiers, Signature[] signatures, Certificate[][] certificates, boolean coreApp, boolean debuggable, boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs, boolean isolatedSplits) { this.codePath = codePath; this.packageName = packageName; this.splitName = splitName; this.isFeatureSplit = isFeatureSplit; this.configForSplit = configForSplit; this.usesSplitName = usesSplitName; this.versionCode = versionCode; this.revisionCode = revisionCode; this.installLocation = installLocation; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); this.signatures = signatures; this.certificates = certificates; this.coreApp = coreApp; this.debuggable = debuggable; this.multiArch = multiArch; this.use32bitAbi = use32bitAbi; this.extractNativeLibs = extractNativeLibs; this.isolatedSplits = isolatedSplits; } } private ParsePackageItemArgs mParseInstrumentationArgs; private ParseComponentArgs mParseActivityArgs; private ParseComponentArgs mParseActivityAliasArgs; private ParseComponentArgs mParseServiceArgs; private ParseComponentArgs mParseProviderArgs; /** If set to true, we will only allow package files that exactly match * the DTD. Otherwise, we try to get as much from the package as we * can without failing. This should normally be set to false, to * support extensions to the DTD in future versions. */ private static final boolean RIGID_PARSER = false; private static final String TAG = "PackageParser"; public PackageParser() { mMetrics = new DisplayMetrics(); mMetrics.setToDefaults(); } public void setSeparateProcesses(String[] procs) { mSeparateProcesses = procs; } /** * Flag indicating this parser should only consider apps with * {@code coreApp} manifest attribute to be valid apps. This is useful when * creating a minimalist boot environment. */ public void setOnlyCoreApps(boolean onlyCoreApps) { mOnlyCoreApps = onlyCoreApps; } public void setDisplayMetrics(DisplayMetrics metrics) { mMetrics = metrics; } /** * Sets the cache directory for this package parser. */ public void setCacheDir(File cacheDir) { mCacheDir = cacheDir; } /** * Callback interface for retrieving information that may be needed while parsing * a package. */ public interface Callback { boolean hasFeature(String feature); String[] getOverlayPaths(String targetPackageName, String targetPath); String[] getOverlayApks(String targetPackageName); } /** * Standard implementation of {@link Callback} on top of the public {@link PackageManager} * class. */ public static final class CallbackImpl implements Callback { private final PackageManager mPm; public CallbackImpl(PackageManager pm) { mPm = pm; } @Override public boolean hasFeature(String feature) { return mPm.hasSystemFeature(feature); } @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) { return null; } @Override public String[] getOverlayApks(String targetPackageName) { return null; } } /** * Set the {@link Callback} that can be used while parsing. */ public void setCallback(Callback cb) { mCallback = cb; } public static final boolean isApkFile(File file) { return isApkPath(file.getName()); } public static boolean isApkPath(String path) { return path.endsWith(".apk"); } /** * Generate and return the {@link PackageInfo} for a parsed package. * * @param p the parsed package. * @param flags indicating which optional information is included. */ public static PackageInfo generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, Set grantedPermissions, PackageUserState state) { return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions, state, UserHandle.getCallingUserId()); } /** * Returns true if the package is installed and not hidden, or if the caller * explicitly wanted all uninstalled and hidden packages as well. * @param appInfo The applicationInfo of the app being checked. */ private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state, ApplicationInfo appInfo) { // If available for the target user, or trying to match uninstalled packages and it's // a system app. return state.isAvailable(flags) || (appInfo != null && appInfo.isSystemApp() && (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0); } public static boolean isAvailable(PackageUserState state) { return checkUseInstalledOrHidden(0, state, null); } public static PackageInfo generatePackageInfo(PackageParser.Package p, int gids[], int flags, long firstInstallTime, long lastUpdateTime, Set grantedPermissions, PackageUserState state, int userId) { if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) { return null; } PackageInfo pi = new PackageInfo(); pi.packageName = p.packageName; pi.splitNames = p.splitNames; pi.versionCode = p.mVersionCode; pi.baseRevisionCode = p.baseRevisionCode; pi.splitRevisionCodes = p.splitRevisionCodes; pi.versionName = p.mVersionName; pi.sharedUserId = p.mSharedUserId; pi.sharedUserLabel = p.mSharedUserLabel; pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; pi.coreApp = p.coreApp; if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { pi.requiredForAllUsers = p.mRequiredForAllUsers; } pi.restrictedAccountType = p.mRestrictedAccountType; pi.requiredAccountType = p.mRequiredAccountType; pi.overlayTarget = p.mOverlayTarget; pi.overlayPriority = p.mOverlayPriority; pi.isStaticOverlay = p.mIsStaticOverlay; pi.firstInstallTime = firstInstallTime; pi.lastUpdateTime = lastUpdateTime; if ((flags&PackageManager.GET_GIDS) != 0) { pi.gids = gids; } if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) { int N = p.configPreferences != null ? p.configPreferences.size() : 0; if (N > 0) { pi.configPreferences = new ConfigurationInfo[N]; p.configPreferences.toArray(pi.configPreferences); } N = p.reqFeatures != null ? p.reqFeatures.size() : 0; if (N > 0) { pi.reqFeatures = new FeatureInfo[N]; p.reqFeatures.toArray(pi.reqFeatures); } N = p.featureGroups != null ? p.featureGroups.size() : 0; if (N > 0) { pi.featureGroups = new FeatureGroupInfo[N]; p.featureGroups.toArray(pi.featureGroups); } } if ((flags & PackageManager.GET_ACTIVITIES) != 0) { final int N = p.activities.size(); if (N > 0) { int num = 0; final ActivityInfo[] res = new ActivityInfo[N]; for (int i = 0; i < N; i++) { final Activity a = p.activities.get(i); if (state.isMatch(a.info, flags)) { res[num++] = generateActivityInfo(a, flags, state, userId); } } pi.activities = ArrayUtils.trimToSize(res, num); } } if ((flags & PackageManager.GET_RECEIVERS) != 0) { final int N = p.receivers.size(); if (N > 0) { int num = 0; final ActivityInfo[] res = new ActivityInfo[N]; for (int i = 0; i < N; i++) { final Activity a = p.receivers.get(i); if (state.isMatch(a.info, flags)) { res[num++] = generateActivityInfo(a, flags, state, userId); } } pi.receivers = ArrayUtils.trimToSize(res, num); } } if ((flags & PackageManager.GET_SERVICES) != 0) { final int N = p.services.size(); if (N > 0) { int num = 0; final ServiceInfo[] res = new ServiceInfo[N]; for (int i = 0; i < N; i++) { final Service s = p.services.get(i); if (state.isMatch(s.info, flags)) { res[num++] = generateServiceInfo(s, flags, state, userId); } } pi.services = ArrayUtils.trimToSize(res, num); } } if ((flags & PackageManager.GET_PROVIDERS) != 0) { final int N = p.providers.size(); if (N > 0) { int num = 0; final ProviderInfo[] res = new ProviderInfo[N]; for (int i = 0; i < N; i++) { final Provider pr = p.providers.get(i); if (state.isMatch(pr.info, flags)) { res[num++] = generateProviderInfo(pr, flags, state, userId); } } pi.providers = ArrayUtils.trimToSize(res, num); } } if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) { int N = p.instrumentation.size(); if (N > 0) { pi.instrumentation = new InstrumentationInfo[N]; for (int i=0; i 0) { pi.permissions = new PermissionInfo[N]; for (int i=0; i 0) { pi.requestedPermissions = new String[N]; pi.requestedPermissionsFlags = new int[N]; for (int i=0; i 0) { pi.signatures = new Signature[N]; System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N); } } return pi; } private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry) throws PackageParserException { InputStream is = null; try { // We must read the stream for the JarEntry to retrieve // its certificates. is = jarFile.getInputStream(entry); readFullyIgnoringContents(is); return jarFile.getCertificateChains(entry); } catch (IOException | RuntimeException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed reading " + entry.getName() + " in " + jarFile, e); } finally { IoUtils.closeQuietly(is); } } public final static int PARSE_IS_SYSTEM = 1<<0; public final static int PARSE_CHATTY = 1<<1; public final static int PARSE_MUST_BE_APK = 1<<2; public final static int PARSE_IGNORE_PROCESSES = 1<<3; public final static int PARSE_FORWARD_LOCK = 1<<4; public final static int PARSE_EXTERNAL_STORAGE = 1<<5; public final static int PARSE_IS_SYSTEM_DIR = 1<<6; public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_COLLECT_CERTIFICATES = 1<<8; public final static int PARSE_TRUSTED_OVERLAY = 1<<9; public final static int PARSE_ENFORCE_CODE = 1<<10; /** @deprecated remove when fixing b/34761192 */ @Deprecated public final static int PARSE_IS_EPHEMERAL = 1<<11; public final static int PARSE_FORCE_SDK = 1<<12; private static final Comparator sSplitNameComparator = new SplitNameComparator(); /** * Used to sort a set of APKs based on their split names, always placing the * base APK (with {@code null} split name) first. */ private static class SplitNameComparator implements Comparator { @Override public int compare(String lhs, String rhs) { if (lhs == null) { return -1; } else if (rhs == null) { return 1; } else { return lhs.compareTo(rhs); } } } /** * Parse only lightweight details about the package at the given location. * Automatically detects if the package is a monolithic style (single APK * file) or cluster style (directory of APKs). *

* This performs sanity checking on cluster style packages, such as * requiring identical package name and version codes, a single base APK, * and unique split names. * * @see PackageParser#parsePackage(File, int) */ public static PackageLite parsePackageLite(File packageFile, int flags) throws PackageParserException { if (packageFile.isDirectory()) { return parseClusterPackageLite(packageFile, flags); } else { return parseMonolithicPackageLite(packageFile, flags); } } private static PackageLite parseMonolithicPackageLite(File packageFile, int flags) throws PackageParserException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); final ApkLite baseApk = parseApkLite(packageFile, flags); final String packagePath = packageFile.getAbsolutePath(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); return new PackageLite(packagePath, baseApk, null, null, null, null, null, null); } static PackageLite parseClusterPackageLite(File packageDir, int flags) throws PackageParserException { final File[] files = packageDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split"); } String packageName = null; int versionCode = 0; Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite"); final ArrayMap apks = new ArrayMap<>(); for (File file : files) { if (isApkFile(file)) { final ApkLite lite = parseApkLite(file, flags); // Assert that all package names and version codes are // consistent with the first one we encounter. if (packageName == null) { packageName = lite.packageName; versionCode = lite.versionCode; } else { if (!packageName.equals(lite.packageName)) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Inconsistent package " + lite.packageName + " in " + file + "; expected " + packageName); } if (versionCode != lite.versionCode) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Inconsistent version " + lite.versionCode + " in " + file + "; expected " + versionCode); } } // Assert that each split is defined only once if (apks.put(lite.splitName, lite) != null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Split name " + lite.splitName + " defined more than once; most recent was " + file); } } } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ApkLite baseApk = apks.remove(null); if (baseApk == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Missing base APK in " + packageDir); } // Always apply deterministic ordering based on splitName final int size = apks.size(); String[] splitNames = null; boolean[] isFeatureSplits = null; String[] usesSplitNames = null; String[] configForSplits = null; String[] splitCodePaths = null; int[] splitRevisionCodes = null; if (size > 0) { splitNames = new String[size]; isFeatureSplits = new boolean[size]; usesSplitNames = new String[size]; configForSplits = new String[size]; splitCodePaths = new String[size]; splitRevisionCodes = new int[size]; splitNames = apks.keySet().toArray(splitNames); Arrays.sort(splitNames, sSplitNameComparator); for (int i = 0; i < size; i++) { final ApkLite apk = apks.get(splitNames[i]); usesSplitNames[i] = apk.usesSplitName; isFeatureSplits[i] = apk.isFeatureSplit; configForSplits[i] = apk.configForSplit; splitCodePaths[i] = apk.codePath; splitRevisionCodes[i] = apk.revisionCode; } } final String codePath = packageDir.getAbsolutePath(); return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes); } /** * Parse the package at the given location. Automatically detects if the * package is a monolithic style (single APK file) or cluster style * (directory of APKs). *

* This performs sanity checking on cluster style packages, such as * requiring identical package name and version codes, a single base APK, * and unique split names. *

* Note that this does not perform signature verification; that * must be done separately in {@link #collectCertificates(Package, int)}. * * If {@code useCaches} is true, the package parser might return a cached * result from a previous parse of the same {@code packageFile} with the same * {@code flags}. Note that this method does not check whether {@code packageFile} * has changed since the last parse, it's up to callers to do so. * * @see #parsePackageLite(File, int) */ public Package parsePackage(File packageFile, int flags, boolean useCaches) throws PackageParserException { Package parsed = useCaches ? getCachedResult(packageFile, flags) : null; if (parsed != null) { return parsed; } if (packageFile.isDirectory()) { parsed = parseClusterPackage(packageFile, flags); } else { parsed = parseMonolithicPackage(packageFile, flags); } cacheResult(packageFile, flags, parsed); return parsed; } /** * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}. */ public Package parsePackage(File packageFile, int flags) throws PackageParserException { return parsePackage(packageFile, flags, false /* useCaches */); } /** * Returns the cache key for a specificied {@code packageFile} and {@code flags}. */ private String getCacheKey(File packageFile, int flags) { StringBuilder sb = new StringBuilder(packageFile.getName()); sb.append('-'); sb.append(flags); return sb.toString(); } @VisibleForTesting protected Package fromCacheEntry(byte[] bytes) throws IOException { Parcel p = Parcel.obtain(); p.unmarshall(bytes, 0, bytes.length); p.setDataPosition(0); PackageParser.Package pkg = new PackageParser.Package(p); p.recycle(); return pkg; } @VisibleForTesting protected byte[] toCacheEntry(Package pkg) throws IOException { Parcel p = Parcel.obtain(); pkg.writeToParcel(p, 0 /* flags */); byte[] serialized = p.marshall(); p.recycle(); return serialized; } /** * Given a {@code packageFile} and a {@code cacheFile} returns whether the * cache file is up to date based on the mod-time of both files. */ private static boolean isCacheUpToDate(File packageFile, File cacheFile) { try { // NOTE: We don't use the File.lastModified API because it has the very // non-ideal failure mode of returning 0 with no excepions thrown. // The nio2 Files API is a little better but is considerably more expensive. final StructStat pkg = android.system.Os.stat(packageFile.getAbsolutePath()); final StructStat cache = android.system.Os.stat(cacheFile.getAbsolutePath()); return pkg.st_mtime < cache.st_mtime; } catch (ErrnoException ee) { // The most common reason why stat fails is that a given cache file doesn't // exist. We ignore that here. It's easy to reason that it's safe to say the // cache isn't up to date if we see any sort of exception here. // // (1) Exception while stating the package file : This should never happen, // and if it does, we do a full package parse (which is likely to throw the // same exception). // (2) Exception while stating the cache file : If the file doesn't exist, the // cache is obviously out of date. If the file *does* exist, we can't read it. // We will attempt to delete and recreate it after parsing the package. if (ee.errno != OsConstants.ENOENT) { Slog.w("Error while stating package cache : ", ee); } return false; } } /** * Returns the cached parse result for {@code packageFile} for parse flags {@code flags}, * or {@code null} if no cached result exists. */ private Package getCachedResult(File packageFile, int flags) { if (mCacheDir == null) { return null; } final String cacheKey = getCacheKey(packageFile, flags); final File cacheFile = new File(mCacheDir, cacheKey); // If the cache is not up to date, return null. if (!isCacheUpToDate(packageFile, cacheFile)) { return null; } try { final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath()); Package p = fromCacheEntry(bytes); if (mCallback != null) { String[] overlayApks = mCallback.getOverlayApks(p.packageName); if (overlayApks != null && overlayApks.length > 0) { for (String overlayApk : overlayApks) { // If a static RRO is updated, return null. if (!isCacheUpToDate(new File(overlayApk), cacheFile)) { return null; } } } } return p; } catch (Exception e) { Slog.w(TAG, "Error reading package cache: ", e); // If something went wrong while reading the cache entry, delete the cache file // so that we regenerate it the next time. cacheFile.delete(); return null; } } /** * Caches the parse result for {@code packageFile} with flags {@code flags}. */ private void cacheResult(File packageFile, int flags, Package parsed) { if (mCacheDir == null) { return; } final String cacheKey = getCacheKey(packageFile, flags); final File cacheFile = new File(mCacheDir, cacheKey); if (cacheFile.exists()) { if (!cacheFile.delete()) { Slog.e(TAG, "Unable to delete cache file: " + cacheFile); } } final byte[] cacheEntry; try { cacheEntry = toCacheEntry(parsed); } catch (IOException ioe) { Slog.e(TAG, "Unable to serialize parsed package for: " + packageFile); return; } if (cacheEntry == null) { return; } try (FileOutputStream fos = new FileOutputStream(cacheFile)) { fos.write(cacheEntry); } catch (IOException ioe) { Slog.w(TAG, "Error writing cache entry.", ioe); cacheFile.delete(); } } /** * Parse all APKs contained in the given directory, treating them as a * single package. This also performs sanity checking, such as requiring * identical package name and version codes, a single base APK, and unique * split names. *

* Note that this does not perform signature verification; that * must be done separately in {@link #collectCertificates(Package, int)}. */ private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException { final PackageLite lite = parseClusterPackageLite(packageDir, 0); if (mOnlyCoreApps && !lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + packageDir); } // Build the split dependency tree. SparseArray splitDependencies = null; final SplitAssetLoader assetLoader; if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) { try { splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite); assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags); } catch (SplitAssetDependencyLoader.IllegalDependencyException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage()); } } else { assetLoader = new DefaultSplitAssetLoader(lite, flags); } try { final AssetManager assets = assetLoader.getBaseAssetManager(); final File baseApk = new File(lite.baseCodePath); final Package pkg = parseBaseApk(baseApk, assets, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseApk); } if (!ArrayUtils.isEmpty(lite.splitNames)) { final int num = lite.splitNames.length; pkg.splitNames = lite.splitNames; pkg.splitCodePaths = lite.splitCodePaths; pkg.splitRevisionCodes = lite.splitRevisionCodes; pkg.splitFlags = new int[num]; pkg.splitPrivateFlags = new int[num]; pkg.applicationInfo.splitNames = pkg.splitNames; pkg.applicationInfo.splitDependencies = splitDependencies; for (int i = 0; i < num; i++) { final AssetManager splitAssets = assetLoader.getSplitAssetManager(i); parseSplitApk(pkg, i, splitAssets, flags); } } pkg.setCodePath(packageDir.getAbsolutePath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; } finally { IoUtils.closeQuietly(assetLoader); } } /** * Parse the given APK file, treating it as as a single monolithic package. *

* Note that this does not perform signature verification; that * must be done separately in {@link #collectCertificates(Package, int)}. * * @deprecated external callers should move to * {@link #parsePackage(File, int)}. Eventually this method will * be marked private. */ @Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { final AssetManager assets = newConfiguredAssetManager(); final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Not a coreApp: " + apkFile); } } try { final Package pkg = parseBaseApk(apkFile, assets, flags); pkg.setCodePath(apkFile.getAbsolutePath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; } finally { IoUtils.closeQuietly(assets); } } private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) throws PackageParserException { if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Invalid package file: " + apkPath); } // The AssetManager guarantees uniqueness for asset paths, so if this asset path // already exists in the AssetManager, addAssetPath will only return the cookie // assigned to it. int cookie = assets.addAssetPath(apkPath); if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Failed adding asset path: " + apkPath); } return cookie; } private Package parseBaseApk(File apkFile, AssetManager assets, int flags) throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); String volumeUuid = null; if (apkPath.startsWith(MNT_EXPAND)) { final int end = apkPath.indexOf('/', MNT_EXPAND.length()); volumeUuid = apkPath.substring(MNT_EXPAND.length(), end); } mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = apkFile.getAbsolutePath(); if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); Resources res = null; XmlResourceParser parser = null; try { res = new Resources(assets, mMetrics, null); parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final String[] outError = new String[1]; final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); if (pkg == null) { throw new PackageParserException(mParseError, apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]); } pkg.setVolumeUuid(volumeUuid); pkg.setApplicationVolumeUuid(volumeUuid); pkg.setBaseCodePath(apkPath); pkg.setSignatures(null); return pkg; } catch (PackageParserException e) { throw e; } catch (Exception e) { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to read manifest from " + apkPath, e); } finally { IoUtils.closeQuietly(parser); } } private void parseSplitApk(Package pkg, int splitIndex, AssetManager assets, int flags) throws PackageParserException { final String apkPath = pkg.splitCodePaths[splitIndex]; mParseError = PackageManager.INSTALL_SUCCEEDED; mArchiveSourcePath = apkPath; if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); final Resources res; XmlResourceParser parser = null; try { res = new Resources(assets, mMetrics, null); assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final String[] outError = new String[1]; pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError); if (pkg == null) { throw new PackageParserException(mParseError, apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]); } } catch (PackageParserException e) { throw e; } catch (Exception e) { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to read manifest from " + apkPath, e); } finally { IoUtils.closeQuietly(parser); } } /** * Parse the manifest of a split APK. *

* Note that split APKs have many more restrictions on what they're capable * of doing, so many valid features of a base APK have been carefully * omitted here. */ private Package parseSplitApk(Package pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex, String[] outError) throws XmlPullParserException, IOException, PackageParserException { AttributeSet attrs = parser; // We parsed manifest tag earlier; just skip past it parsePackageSplitNames(parser, attrs); mParseInstrumentationArgs = null; mParseActivityArgs = null; mParseServiceArgs = null; mParseProviderArgs = null; int type; boolean foundApp = false; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (tagName.equals(TAG_APPLICATION)) { if (foundApp) { if (RIGID_PARSER) { outError[0] = " has more than one "; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, " has more than one "); XmlUtils.skipCurrentTag(parser); continue; } } foundApp = true; if (!parseSplitApplication(pkg, res, parser, flags, splitIndex, outError)) { return null; } } else if (RIGID_PARSER) { outError[0] = "Bad element under : " + parser.getName(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } } if (!foundApp) { outError[0] = " does not contain an "; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY; } return pkg; } public static int getApkSigningVersion(Package pkg) { try { if (ApkSignatureSchemeV2Verifier.hasSignature(pkg.baseCodePath)) { return APK_SIGNING_V2; } return APK_SIGNING_V1; } catch (IOException e) { } return APK_SIGNING_UNKNOWN; } /** * Populates the correct packages fields with the given certificates. *

* This is useful when we've already processed the certificates [such as during package * installation through an installer session]. We don't re-process the archive and * simply populate the correct fields. */ public static void populateCertificates(Package pkg, Certificate[][] certificates) throws PackageParserException { pkg.mCertificates = null; pkg.mSignatures = null; pkg.mSigningKeys = null; pkg.mCertificates = certificates; try { pkg.mSignatures = convertToSignatures(certificates); } catch (CertificateEncodingException e) { // certificates weren't encoded properly; something went wrong throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + pkg.baseCodePath, e); } pkg.mSigningKeys = new ArraySet<>(certificates.length); for (int i = 0; i < certificates.length; i++) { Certificate[] signerCerts = certificates[i]; Certificate signerCert = signerCerts[0]; pkg.mSigningKeys.add(signerCert.getPublicKey()); } // add signatures to child packages final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { Package childPkg = pkg.childPackages.get(i); childPkg.mCertificates = pkg.mCertificates; childPkg.mSignatures = pkg.mSignatures; childPkg.mSigningKeys = pkg.mSigningKeys; } } /** * Collect certificates from all the APKs described in the given package, * populating {@link Package#mSignatures}. Also asserts that all APK * contents are signed correctly and consistently. */ public static void collectCertificates(Package pkg, int parseFlags) throws PackageParserException { collectCertificatesInternal(pkg, parseFlags); final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0; for (int i = 0; i < childCount; i++) { Package childPkg = pkg.childPackages.get(i); childPkg.mCertificates = pkg.mCertificates; childPkg.mSignatures = pkg.mSignatures; childPkg.mSigningKeys = pkg.mSigningKeys; } } private static void collectCertificatesInternal(Package pkg, int parseFlags) throws PackageParserException { pkg.mCertificates = null; pkg.mSignatures = null; pkg.mSigningKeys = null; Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); try { collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags); if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { for (int i = 0; i < pkg.splitCodePaths.length; i++) { collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags); } } } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private static void collectCertificates(Package pkg, File apkFile, int parseFlags) throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); // Try to verify the APK using APK Signature Scheme v2. boolean verified = false; { Certificate[][] allSignersCerts = null; Signature[] signatures = null; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2"); allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath); signatures = convertToSignatures(allSignersCerts); // APK verified using APK Signature Scheme v2. verified = true; } catch (ApkSignatureSchemeV2Verifier.SignatureNotFoundException e) { // No APK Signature Scheme v2 signature found if ((parseFlags & PARSE_IS_EPHEMERAL) != 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "No APK Signature Scheme v2 signature in ephemeral package " + apkPath, e); } // Static shared libraries must use only the V2 signing scheme if (pkg.applicationInfo.isStaticSharedLibrary()) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Static shared libs must use v2 signature scheme " + apkPath); } } catch (Exception e) { // APK Signature Scheme v2 signature was found but did not verify throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath + " using APK Signature Scheme v2", e); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } if (verified) { if (pkg.mCertificates == null) { pkg.mCertificates = allSignersCerts; pkg.mSignatures = signatures; pkg.mSigningKeys = new ArraySet<>(allSignersCerts.length); for (int i = 0; i < allSignersCerts.length; i++) { Certificate[] signerCerts = allSignersCerts[i]; Certificate signerCert = signerCerts[0]; pkg.mSigningKeys.add(signerCert.getPublicKey()); } } else { if (!Signature.areExactMatch(pkg.mSignatures, signatures)) { throw new PackageParserException( INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, apkPath + " has mismatched certificates"); } } // Not yet done, because we need to confirm that AndroidManifest.xml exists and, // if requested, that classes.dex exists. } } StrictJarFile jarFile = null; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor"); // Ignore signature stripping protections when verifying APKs from system partition. // For those APKs we only care about extracting signer certificates, and don't care // about verifying integrity. boolean signatureSchemeRollbackProtectionsEnforced = (parseFlags & PARSE_IS_SYSTEM_DIR) == 0; jarFile = new StrictJarFile( apkPath, !verified, // whether to verify JAR signature signatureSchemeRollbackProtectionsEnforced); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Always verify manifest, regardless of source final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (manifestEntry == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Package " + apkPath + " has no manifest"); } // Optimization: early termination when APK already verified if (verified) { return; } // APK's integrity needs to be verified using JAR signature scheme. Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV1"); final List toVerify = new ArrayList<>(); toVerify.add(manifestEntry); // If we're parsing an untrusted package, verify all contents if ((parseFlags & PARSE_IS_SYSTEM_DIR) == 0) { final Iterator i = jarFile.iterator(); while (i.hasNext()) { final ZipEntry entry = i.next(); if (entry.isDirectory()) continue; final String entryName = entry.getName(); if (entryName.startsWith("META-INF/")) continue; if (entryName.equals(ANDROID_MANIFEST_FILENAME)) continue; toVerify.add(entry); } } // Verify that entries are signed consistently with the first entry // we encountered. Note that for splits, certificates may have // already been populated during an earlier parse of a base APK. for (ZipEntry entry : toVerify) { final Certificate[][] entryCerts = loadCertificates(jarFile, entry); if (ArrayUtils.isEmpty(entryCerts)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package " + apkPath + " has no certificates at entry " + entry.getName()); } final Signature[] entrySignatures = convertToSignatures(entryCerts); if (pkg.mCertificates == null) { pkg.mCertificates = entryCerts; pkg.mSignatures = entrySignatures; pkg.mSigningKeys = new ArraySet(); for (int i=0; i < entryCerts.length; i++) { pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey()); } } else { if (!Signature.areExactMatch(pkg.mSignatures, entrySignatures)) { throw new PackageParserException( INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath + " has mismatched certificates at entry " + entry.getName()); } } } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } catch (GeneralSecurityException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, "Failed to collect certificates from " + apkPath, e); } catch (IOException | RuntimeException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Failed to collect certificates from " + apkPath, e); } finally { closeQuietly(jarFile); } } private static Signature[] convertToSignatures(Certificate[][] certs) throws CertificateEncodingException { final Signature[] res = new Signature[certs.length]; for (int i = 0; i < certs.length; i++) { res[i] = new Signature(certs[i]); } return res; } private static AssetManager newConfiguredAssetManager() { AssetManager assetManager = new AssetManager(); assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); return assetManager; } /** * Utility method that retrieves lightweight details about a single APK * file, including package name, split name, and install location. * * @param apkFile path to a single APK * @param flags optional parse flags, such as * {@link #PARSE_COLLECT_CERTIFICATES} */ public static ApkLite parseApkLite(File apkFile, int flags) throws PackageParserException { final String apkPath = apkFile.getAbsolutePath(); AssetManager assets = null; XmlResourceParser parser = null; try { assets = newConfiguredAssetManager(); int cookie = assets.addAssetPath(apkPath); if (cookie == 0) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } final DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final Signature[] signatures; final Certificate[][] certificates; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { // TODO: factor signature related items out of Package object final Package tempPkg = new Package((String) null); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); try { collectCertificates(tempPkg, apkFile, flags); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } signatures = tempPkg.mSignatures; certificates = tempPkg.mCertificates; } else { signatures = null; certificates = null; } final AttributeSet attrs = parser; return parseApkLite(apkPath, parser, attrs, flags, signatures, certificates); } catch (XmlPullParserException | IOException | RuntimeException e) { Slog.w(TAG, "Failed to parse " + apkPath, e); throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); IoUtils.closeQuietly(assets); } } private static String validateName(String name, boolean requireSeparator, boolean requireFilename) { final int N = name.length(); boolean hasSep = false; boolean front = true; for (int i=0; i= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { front = false; continue; } if (!front) { if ((c >= '0' && c <= '9') || c == '_') { continue; } } if (c == '.') { hasSep = true; front = true; continue; } return "bad character '" + c + "'"; } if (requireFilename && !FileUtils.isValidExtFilename(name)) { return "Invalid filename"; } return hasSep || !requireSeparator ? null : "must have at least one '.' separator"; } private static Pair parsePackageSplitNames(XmlPullParser parser, AttributeSet attrs) throws IOException, XmlPullParserException, PackageParserException { int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { } if (type != XmlPullParser.START_TAG) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "No start tag found"); } if (!parser.getName().equals(TAG_MANIFEST)) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "No tag"); } final String packageName = attrs.getAttributeValue(null, "package"); if (!"android".equals(packageName)) { final String error = validateName(packageName, true, true); if (error != null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, "Invalid manifest package: " + error); } } String splitName = attrs.getAttributeValue(null, "split"); if (splitName != null) { if (splitName.length() == 0) { splitName = null; } else { final String error = validateName(splitName, false, false); if (error != null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, "Invalid manifest split: " + error); } } } return Pair.create(packageName.intern(), (splitName != null) ? splitName.intern() : splitName); } private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs, int flags, Signature[] signatures, Certificate[][] certificates) throws IOException, XmlPullParserException, PackageParserException { final Pair packageSplit = parsePackageSplitNames(parser, attrs); int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; int versionCode = 0; int revisionCode = 0; boolean coreApp = false; boolean debuggable = false; boolean multiArch = false; boolean use32bitAbi = false; boolean extractNativeLibs = true; boolean isolatedSplits = false; boolean isFeatureSplit = false; String configForSplit = null; String usesSplitName = null; for (int i = 0; i < attrs.getAttributeCount(); i++) { final String attr = attrs.getAttributeName(i); if (attr.equals("installLocation")) { installLocation = attrs.getAttributeIntValue(i, PARSE_DEFAULT_INSTALL_LOCATION); } else if (attr.equals("versionCode")) { versionCode = attrs.getAttributeIntValue(i, 0); } else if (attr.equals("revisionCode")) { revisionCode = attrs.getAttributeIntValue(i, 0); } else if (attr.equals("coreApp")) { coreApp = attrs.getAttributeBooleanValue(i, false); } else if (attr.equals("isolatedSplits")) { isolatedSplits = attrs.getAttributeBooleanValue(i, false); } else if (attr.equals("configForSplit")) { configForSplit = attrs.getAttributeValue(i); } else if (attr.equals("isFeatureSplit")) { isFeatureSplit = attrs.getAttributeBooleanValue(i, false); } } // Only search the tree when the tag is directly below int type; final int searchDepth = parser.getDepth() + 1; final List verifiers = new ArrayList(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (parser.getDepth() != searchDepth) { continue; } if (TAG_PACKAGE_VERIFIER.equals(parser.getName())) { final VerifierInfo verifier = parseVerifier(attrs); if (verifier != null) { verifiers.add(verifier); } } else if (TAG_APPLICATION.equals(parser.getName())) { for (int i = 0; i < attrs.getAttributeCount(); ++i) { final String attr = attrs.getAttributeName(i); if ("debuggable".equals(attr)) { debuggable = attrs.getAttributeBooleanValue(i, false); } if ("multiArch".equals(attr)) { multiArch = attrs.getAttributeBooleanValue(i, false); } if ("use32bitAbi".equals(attr)) { use32bitAbi = attrs.getAttributeBooleanValue(i, false); } if ("extractNativeLibs".equals(attr)) { extractNativeLibs = attrs.getAttributeBooleanValue(i, true); } } } else if (TAG_USES_SPLIT.equals(parser.getName())) { if (usesSplitName != null) { Slog.w(TAG, "Only one permitted. Ignoring others."); continue; } usesSplitName = attrs.getAttributeValue(ANDROID_RESOURCES, "name"); if (usesSplitName == null) { throw new PackageParserException( PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, " tag requires 'android:name' attribute"); } } } return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, configForSplit, usesSplitName, versionCode, revisionCode, installLocation, verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs, isolatedSplits); } /** * Temporary. */ static public Signature stringToSignature(String str) { final int N = str.length(); byte[] sig = new byte[N]; for (int i=0; i MAX_PACKAGES_PER_APK) { outError[0] = "Maximum number of packages per APK is: " + MAX_PACKAGES_PER_APK; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } // Make sure we have a valid child package name String childPackageName = parser.getAttributeValue(null, "package"); if (validateName(childPackageName, true, false) != null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; return false; } // Child packages must be unique if (childPackageName.equals(parentPkg.packageName)) { String message = "Child package name cannot be equal to parent package name: " + parentPkg.packageName; Slog.w(TAG, message); outError[0] = message; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } // Child packages must be unique if (parentPkg.hasChildPackage(childPackageName)) { String message = "Duplicate child package:" + childPackageName; Slog.w(TAG, message); outError[0] = message; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } // Go ahead and parse the child Package childPkg = new Package(childPackageName); // Child package inherits parent version code/name/target SDK childPkg.mVersionCode = parentPkg.mVersionCode; childPkg.baseRevisionCode = parentPkg.baseRevisionCode; childPkg.mVersionName = parentPkg.mVersionName; childPkg.applicationInfo.targetSdkVersion = parentPkg.applicationInfo.targetSdkVersion; childPkg.applicationInfo.minSdkVersion = parentPkg.applicationInfo.minSdkVersion; childPkg = parseBaseApkCommon(childPkg, CHILD_PACKAGE_TAGS, res, parser, flags, outError); if (childPkg == null) { // If we got null then error was set during child parsing return false; } // Set the parent-child relation if (parentPkg.childPackages == null) { parentPkg.childPackages = new ArrayList<>(); } parentPkg.childPackages.add(childPkg); childPkg.parentPackage = parentPkg; return true; } /** * Parse the manifest of a base APK. When adding new features you * need to consider whether they should be supported by split APKs and child * packages. * * @param apkPath The package apk file path * @param res The resources from which to resolve values * @param parser The manifest parser * @param flags Flags how to parse * @param outError Human readable error message * @return Parsed package or null on error. * * @throws XmlPullParserException * @throws IOException */ private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final String splitName; final String pkgName; try { Pair packageSplit = parsePackageSplitNames(parser, parser); pkgName = packageSplit.first; splitName = packageSplit.second; if (!TextUtils.isEmpty(splitName)) { outError[0] = "Expected base APK, but found split " + splitName; mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; return null; } } catch (PackageParserException e) { mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; return null; } if (mCallback != null) { String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath); if (overlayPaths != null && overlayPaths.length > 0) { for (String overlayPath : overlayPaths) { res.getAssets().addOverlayPath(overlayPath); } } } final Package pkg = new Package(pkgName); TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifest); pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_versionCode, 0); pkg.baseRevisionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_revisionCode, 0); pkg.mVersionName = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifest_versionName, 0); if (pkg.mVersionName != null) { pkg.mVersionName = pkg.mVersionName.intern(); } pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false); sa.recycle(); return parseBaseApkCommon(pkg, null, res, parser, flags, outError); } /** * This is the common parsing routing for handling parent and child * packages in a base APK. The difference between parent and child * parsing is that some tags are not supported by child packages as * well as some manifest attributes are ignored. The implementation * assumes the calling code has already handled the manifest tag if needed * (this applies to the parent only). * * @param pkg The package which to populate * @param acceptedTags Which tags to handle, null to handle all * @param res Resources against which to resolve values * @param parser Parser of the manifest * @param flags Flags about how to parse * @param outError Human readable error if parsing fails * @return The package if parsing succeeded or null. * * @throws XmlPullParserException * @throws IOException */ private Package parseBaseApkCommon(Package pkg, Set acceptedTags, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { mParseInstrumentationArgs = null; mParseActivityArgs = null; mParseServiceArgs = null; mParseProviderArgs = null; int type; boolean foundApp = false; TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifest); String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); if (str != null && str.length() > 0) { if ((flags & PARSE_IS_EPHEMERAL) != 0) { outError[0] = "sharedUserId not allowed in ephemeral application"; mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID; return null; } String nameError = validateName(str, true, false); if (nameError != null && !"android".equals(pkg.packageName)) { outError[0] = " specifies bad sharedUserId name \"" + str + "\": " + nameError; mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID; return null; } pkg.mSharedUserId = str.intern(); pkg.mSharedUserLabel = sa.getResourceId( com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0); } pkg.installLocation = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_installLocation, PARSE_DEFAULT_INSTALL_LOCATION); pkg.applicationInfo.installLocation = pkg.installLocation; final int targetSandboxVersion = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_targetSandboxVersion, PARSE_DEFAULT_TARGET_SANDBOX); pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion; /* Set the global "forward lock" flag */ if ((flags & PARSE_FORWARD_LOCK) != 0) { pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK; } /* Set the global "on SD card" flag */ if ((flags & PARSE_EXTERNAL_STORAGE) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; } if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) { pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; } // Resource boolean are -1, so 1 means we don't know the value. int supportsSmallScreens = 1; int supportsNormalScreens = 1; int supportsLargeScreens = 1; int supportsXLargeScreens = 1; int resizeable = 1; int anyDensity = 1; int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (acceptedTags != null && !acceptedTags.contains(tagName)) { Slog.w(TAG, "Skipping unsupported element under : " + tagName + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } if (tagName.equals(TAG_APPLICATION)) { if (foundApp) { if (RIGID_PARSER) { outError[0] = " has more than one "; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, " has more than one "); XmlUtils.skipCurrentTag(parser); continue; } } foundApp = true; if (!parseBaseApplication(pkg, res, parser, flags, outError)) { return null; } } else if (tagName.equals(TAG_OVERLAY)) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestResourceOverlay); pkg.mOverlayTarget = sa.getString( com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage); pkg.mOverlayPriority = sa.getInt( com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority, 0); pkg.mIsStaticOverlay = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic, false); final String propName = sa.getString( com.android.internal.R.styleable .AndroidManifestResourceOverlay_requiredSystemPropertyName); final String propValue = sa.getString( com.android.internal.R.styleable .AndroidManifestResourceOverlay_requiredSystemPropertyValue); sa.recycle(); if (pkg.mOverlayTarget == null) { outError[0] = " does not specify a target package"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) { outError[0] = " priority must be between 0 and 9999"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } // check to see if overlay should be excluded based on system property condition if (!checkOverlayRequiredSystemProperty(propName, propValue)) { Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and " + pkg.baseCodePath+ ": overlay ignored due to required system property: " + propName + " with value: " + propValue); return null; } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_KEY_SETS)) { if (!parseKeySets(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_PERMISSION_GROUP)) { if (!parsePermissionGroup(pkg, flags, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_PERMISSION)) { if (!parsePermission(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_PERMISSION_TREE)) { if (!parsePermissionTree(pkg, res, parser, outError)) { return null; } } else if (tagName.equals(TAG_USES_PERMISSION)) { if (!parseUsesPermission(pkg, res, parser)) { return null; } } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M) || tagName.equals(TAG_USES_PERMISSION_SDK_23)) { if (!parseUsesPermission(pkg, res, parser)) { return null; } } else if (tagName.equals(TAG_USES_CONFIGURATION)) { ConfigurationInfo cPref = new ConfigurationInfo(); sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesConfiguration); cPref.reqTouchScreen = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen, Configuration.TOUCHSCREEN_UNDEFINED); cPref.reqKeyboardType = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType, Configuration.KEYBOARD_UNDEFINED); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard, false)) { cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; } cPref.reqNavigation = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation, Configuration.NAVIGATION_UNDEFINED); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav, false)) { cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; } sa.recycle(); pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_USES_FEATURE)) { FeatureInfo fi = parseUsesFeature(res, parser); pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi); if (fi.name == null) { ConfigurationInfo cPref = new ConfigurationInfo(); cPref.reqGlEsVersion = fi.reqGlEsVersion; pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref); } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_FEATURE_GROUP)) { FeatureGroupInfo group = new FeatureGroupInfo(); ArrayList features = null; final int innerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } final String innerTagName = parser.getName(); if (innerTagName.equals("uses-feature")) { FeatureInfo featureInfo = parseUsesFeature(res, parser); // FeatureGroups are stricter and mandate that // any declared are mandatory. featureInfo.flags |= FeatureInfo.FLAG_REQUIRED; features = ArrayUtils.add(features, featureInfo); } else { Slog.w(TAG, "Unknown element under : " + innerTagName + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } XmlUtils.skipCurrentTag(parser); } if (features != null) { group.features = new FeatureInfo[features.size()]; group.features = features.toArray(group.features); } pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group); } else if (tagName.equals(TAG_USES_SDK)) { if (SDK_VERSION > 0) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesSdk); int minVers = 1; String minCode = null; int targetVers = 0; String targetCode = null; TypedValue val = sa.peekValue( com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion); if (val != null) { if (val.type == TypedValue.TYPE_STRING && val.string != null) { targetCode = minCode = val.string.toString(); } else { // If it's not a string, it's an integer. targetVers = minVers = val.data; } } val = sa.peekValue( com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion); if (val != null) { if (val.type == TypedValue.TYPE_STRING && val.string != null) { targetCode = val.string.toString(); if (minCode == null) { minCode = targetCode; } } else { // If it's not a string, it's an integer. targetVers = val.data; } } sa.recycle(); final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode, SDK_VERSION, SDK_CODENAMES, outError); if (minSdkVersion < 0) { mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; } final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers, targetCode, SDK_VERSION, SDK_CODENAMES, outError); if (targetSdkVersion < 0) { mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; return null; } pkg.applicationInfo.minSdkVersion = minSdkVersion; pkg.applicationInfo.targetSdkVersion = targetSdkVersion; } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_SUPPORT_SCREENS)) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestSupportsScreens); pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, 0); pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, 0); pkg.applicationInfo.largestWidthLimitDp = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, 0); // This is a trick to get a boolean and still able to detect // if a value was actually set. supportsSmallScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens, supportsSmallScreens); supportsNormalScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens, supportsNormalScreens); supportsLargeScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens, supportsLargeScreens); supportsXLargeScreens = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens, supportsXLargeScreens); resizeable = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable, resizeable); anyDensity = sa.getInteger( com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity, anyDensity); sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_PROTECTED_BROADCAST)) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestProtectedBroadcast); // Note: don't allow this value to be a reference to a resource // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name); sa.recycle(); if (name != null && (flags&PARSE_IS_SYSTEM) != 0) { if (pkg.protectedBroadcasts == null) { pkg.protectedBroadcasts = new ArrayList(); } if (!pkg.protectedBroadcasts.contains(name)) { pkg.protectedBroadcasts.add(name.intern()); } } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_INSTRUMENTATION)) { if (parseInstrumentation(pkg, res, parser, outError) == null) { return null; } } else if (tagName.equals(TAG_ORIGINAL_PACKAGE)) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestOriginalPackage); String orig =sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); if (!pkg.packageName.equals(orig)) { if (pkg.mOriginalPackages == null) { pkg.mOriginalPackages = new ArrayList(); pkg.mRealPackage = pkg.packageName; } pkg.mOriginalPackages.add(orig); } sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_ADOPT_PERMISSIONS)) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestOriginalPackage); String name = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); sa.recycle(); if (name != null) { if (pkg.mAdoptPermissions == null) { pkg.mAdoptPermissions = new ArrayList(); } pkg.mAdoptPermissions.add(name); } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals(TAG_USES_GL_TEXTURE)) { // Just skip this tag XmlUtils.skipCurrentTag(parser); continue; } else if (tagName.equals(TAG_COMPATIBLE_SCREENS)) { // Just skip this tag XmlUtils.skipCurrentTag(parser); continue; } else if (tagName.equals(TAG_SUPPORTS_INPUT)) {// XmlUtils.skipCurrentTag(parser); continue; } else if (tagName.equals(TAG_EAT_COMMENT)) { // Just skip this tag XmlUtils.skipCurrentTag(parser); continue; } else if (tagName.equals(TAG_PACKAGE)) { if (!MULTI_PACKAGE_APK_ENABLED) { XmlUtils.skipCurrentTag(parser); continue; } if (!parseBaseApkChild(pkg, res, parser, flags, outError)) { // If parsing a child failed the error is already set return null; } } else if (tagName.equals(TAG_RESTRICT_UPDATE)) { if ((flags & PARSE_IS_SYSTEM_DIR) != 0) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestRestrictUpdate); final String hash = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestRestrictUpdate_hash, 0); sa.recycle(); pkg.restrictUpdateHash = null; if (hash != null) { final int hashLength = hash.length(); final byte[] hashBytes = new byte[hashLength / 2]; for (int i = 0; i < hashLength; i += 2){ hashBytes[i/2] = (byte) ((Character.digit(hash.charAt(i), 16) << 4) + Character.digit(hash.charAt(i + 1), 16)); } pkg.restrictUpdateHash = hashBytes; } } XmlUtils.skipCurrentTag(parser); } else if (RIGID_PARSER) { outError[0] = "Bad element under : " + parser.getName(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } else { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } } if (!foundApp && pkg.instrumentation.size() == 0) { outError[0] = " does not contain an or "; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY; } final int NP = PackageParser.NEW_PERMISSIONS.length; StringBuilder implicitPerms = null; for (int ip=0; ip= npi.sdkVersion) { break; } if (!pkg.requestedPermissions.contains(npi.name)) { if (implicitPerms == null) { implicitPerms = new StringBuilder(128); implicitPerms.append(pkg.packageName); implicitPerms.append(": compat added "); } else { implicitPerms.append(' '); } implicitPerms.append(npi.name); pkg.requestedPermissions.add(npi.name); } } if (implicitPerms != null) { Slog.i(TAG, implicitPerms.toString()); } final int NS = PackageParser.SPLIT_PERMISSIONS.length; for (int is=0; is= spi.targetSdk || !pkg.requestedPermissions.contains(spi.rootPerm)) { continue; } for (int in=0; in 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS; } if (supportsNormalScreens != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS; } if (supportsLargeScreens < 0 || (supportsLargeScreens > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; } if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.GINGERBREAD)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS; } if (resizeable < 0 || (resizeable > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS; } if (anyDensity < 0 || (anyDensity > 0 && pkg.applicationInfo.targetSdkVersion >= android.os.Build.VERSION_CODES.DONUT)) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; } // At this point we can check if an application is not supporting densities and hence // cannot be windowed / resized. Note that an SDK version of 0 is common for // pre-Doughnut applications. if (pkg.applicationInfo.usesCompatibilityMode()) { adjustPackageToBeUnresizeableAndUnpipable(pkg); } return pkg; } private boolean checkOverlayRequiredSystemProperty(String propName, String propValue) { if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) { if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) { // malformed condition - incomplete Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName + "=" + propValue + "' - require both requiredSystemPropertyName" + " AND requiredSystemPropertyValue to be specified."); return false; } // no valid condition set - so no exclusion criteria, overlay will be included. return true; } // check property value - make sure it is both set and equal to expected value final String currValue = SystemProperties.get(propName); return (currValue != null && currValue.equals(propValue)); } /** * This is a pre-density application which will get scaled - instead of being pixel perfect. * This type of application is not resizable. * * @param pkg The package which needs to be marked as unresizable. */ private void adjustPackageToBeUnresizeableAndUnpipable(Package pkg) { for (Activity a : pkg.activities) { a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE; a.info.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE; } } /** * Computes the targetSdkVersion to use at runtime. If the package is not * compatible with this platform, populates {@code outError[0]} with an * error message. *

* If {@code targetCode} is not specified, e.g. the value is {@code null}, * then the {@code targetVers} will be returned unmodified. *

* Otherwise, the behavior varies based on whether the current platform * is a pre-release version, e.g. the {@code platformSdkCodenames} array * has length > 0: *

    *
  • If this is a pre-release platform and the value specified by * {@code targetCode} is contained within the array of allowed pre-release * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}. *
  • If this is a released platform, this method will return -1 to * indicate that the package is not compatible with this platform. *
* * @param targetVers targetSdkVersion number, if specified in the * application manifest, or 0 otherwise * @param targetCode targetSdkVersion code, if specified in the application * manifest, or {@code null} otherwise * @param platformSdkVersion platform SDK version number, typically * Build.VERSION.SDK_INT * @param platformSdkCodenames array of allowed pre-release SDK codenames * for this platform * @param outError output array to populate with error, if applicable * @return the targetSdkVersion to use at runtime, or -1 if the package is * not compatible with this platform * @hide Exposed for unit testing only. */ @TestApi public static int computeTargetSdkVersion(@IntRange(from = 0) int targetVers, @Nullable String targetCode, @IntRange(from = 1) int platformSdkVersion, @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { // If it's a release SDK, return the version number unmodified. if (targetCode == null) { return targetVers; } // If it's a pre-release SDK and the codename matches this platform, it // definitely targets this SDK. if (ArrayUtils.contains(platformSdkCodenames, targetCode)) { return Build.VERSION_CODES.CUR_DEVELOPMENT; } // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode + " (current platform is any of " + Arrays.toString(platformSdkCodenames) + ")"; } else { outError[0] = "Requires development platform " + targetCode + " but this is a release platform."; } return -1; } /** * Computes the minSdkVersion to use at runtime. If the package is not * compatible with this platform, populates {@code outError[0]} with an * error message. *

* If {@code minCode} is not specified, e.g. the value is {@code null}, * then behavior varies based on the {@code platformSdkVersion}: *

    *
  • If the platform SDK version is greater than or equal to the * {@code minVers}, returns the {@code mniVers} unmodified. *
  • Otherwise, returns -1 to indicate that the package is not * compatible with this platform. *
*

* Otherwise, the behavior varies based on whether the current platform * is a pre-release version, e.g. the {@code platformSdkCodenames} array * has length > 0: *

    *
  • If this is a pre-release platform and the value specified by * {@code targetCode} is contained within the array of allowed pre-release * codenames, this method will return {@link Build.VERSION_CODES#CUR_DEVELOPMENT}. *
  • If this is a released platform, this method will return -1 to * indicate that the package is not compatible with this platform. *
* * @param minVers minSdkVersion number, if specified in the application * manifest, or 1 otherwise * @param minCode minSdkVersion code, if specified in the application * manifest, or {@code null} otherwise * @param platformSdkVersion platform SDK version number, typically * Build.VERSION.SDK_INT * @param platformSdkCodenames array of allowed prerelease SDK codenames * for this platform * @param outError output array to populate with error, if applicable * @return the minSdkVersion to use at runtime, or -1 if the package is not * compatible with this platform * @hide Exposed for unit testing only. */ @TestApi public static int computeMinSdkVersion(@IntRange(from = 1) int minVers, @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, @NonNull String[] platformSdkCodenames, @NonNull String[] outError) { // If it's a release SDK, make sure we meet the minimum SDK requirement. if (minCode == null) { if (minVers <= platformSdkVersion) { return minVers; } // We don't meet the minimum SDK requirement. outError[0] = "Requires newer sdk version #" + minVers + " (current version is #" + platformSdkVersion + ")"; return -1; } // If it's a pre-release SDK and the codename matches this platform, we // definitely meet the minimum SDK requirement. if (ArrayUtils.contains(platformSdkCodenames, minCode)) { return Build.VERSION_CODES.CUR_DEVELOPMENT; } // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode + " (current platform is any of " + Arrays.toString(platformSdkCodenames) + ")"; } else { outError[0] = "Requires development platform " + minCode + " but this is a release platform."; } return -1; } private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs) { FeatureInfo fi = new FeatureInfo(); TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesFeature); // Note: don't allow this value to be a reference to a resource // that may change. fi.name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesFeature_name); fi.version = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesFeature_version, 0); if (fi.name == null) { fi.reqGlEsVersion = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion, FeatureInfo.GL_ES_VERSION_UNDEFINED); } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesFeature_required, true)) { fi.flags |= FeatureInfo.FLAG_REQUIRED; } sa.recycle(); return fi; } private boolean parseUsesStaticLibrary(Package pkg, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary); // Note: don't allow this value to be a reference to a resource that may change. String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); final int version = sa.getInt( com.android.internal.R.styleable.AndroidManifestUsesStaticLibrary_version, -1); String certSha256 = sa.getNonResourceString(com.android.internal.R.styleable .AndroidManifestUsesStaticLibrary_certDigest); sa.recycle(); // Since an APK providing a static shared lib can only provide the lib - fail if malformed if (lname == null || version < 0 || certSha256 == null) { outError[0] = "Bad uses-static-library declaration name: " + lname + " version: " + version + " certDigest" + certSha256; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; XmlUtils.skipCurrentTag(parser); return false; } // Can depend only on one version of the same library if (pkg.usesStaticLibraries != null && pkg.usesStaticLibraries.contains(lname)) { outError[0] = "Depending on multiple versions of static library " + lname; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; XmlUtils.skipCurrentTag(parser); return false; } lname = lname.intern(); // We allow ":" delimiters in the SHA declaration as this is the format // emitted by the certtool making it easy for developers to copy/paste. certSha256 = certSha256.replace(":", "").toLowerCase(); pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname); pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt( pkg.usesStaticLibrariesVersions, version, true); pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String.class, pkg.usesStaticLibrariesCertDigests, certSha256, true); XmlUtils.skipCurrentTag(parser); return true; } private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesPermission); // Note: don't allow this value to be a reference to a resource // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); int maxSdkVersion = 0; TypedValue val = sa.peekValue( com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion); if (val != null) { if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) { maxSdkVersion = val.data; } } final String requiredFeature = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); final String requiredNotfeature = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0); sa.recycle(); XmlUtils.skipCurrentTag(parser); if (name == null) { return true; } if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) { return true; } // Only allow requesting this permission if the platform supports the given feature. if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(requiredFeature)) { return true; } // Only allow requesting this permission if the platform doesn't support the given feature. if (requiredNotfeature != null && mCallback != null && mCallback.hasFeature(requiredNotfeature)) { return true; } int index = pkg.requestedPermissions.indexOf(name); if (index == -1) { pkg.requestedPermissions.add(name.intern()); } else { Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " + name + " in package: " + pkg.packageName + " at: " + parser.getPositionDescription()); } return true; } private static String buildClassName(String pkg, CharSequence clsSeq, String[] outError) { if (clsSeq == null || clsSeq.length() <= 0) { outError[0] = "Empty class name in package " + pkg; return null; } String cls = clsSeq.toString(); char c = cls.charAt(0); if (c == '.') { return pkg + cls; } if (cls.indexOf('.') < 0) { StringBuilder b = new StringBuilder(pkg); b.append('.'); b.append(cls); return b.toString(); } return cls; } private static String buildCompoundName(String pkg, CharSequence procSeq, String type, String[] outError) { String proc = procSeq.toString(); char c = proc.charAt(0); if (pkg != null && c == ':') { if (proc.length() < 2) { outError[0] = "Bad " + type + " name " + proc + " in package " + pkg + ": must be at least two characters"; return null; } String subName = proc.substring(1); String nameError = validateName(subName, false, false); if (nameError != null) { outError[0] = "Invalid " + type + " name " + proc + " in package " + pkg + ": " + nameError; return null; } return pkg + proc; } String nameError = validateName(proc, true, false); if (nameError != null && !"system".equals(proc)) { outError[0] = "Invalid " + type + " name " + proc + " in package " + pkg + ": " + nameError; return null; } return proc; } private static String buildProcessName(String pkg, String defProc, CharSequence procSeq, int flags, String[] separateProcesses, String[] outError) { if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) { return defProc != null ? defProc : pkg; } if (separateProcesses != null) { for (int i=separateProcesses.length-1; i>=0; i--) { String sp = separateProcesses[i]; if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) { return pkg; } } } if (procSeq == null || procSeq.length() <= 0) { return defProc; } return buildCompoundName(pkg, procSeq, "process", outError); } private static String buildTaskAffinityName(String pkg, String defProc, CharSequence procSeq, String[] outError) { if (procSeq == null) { return defProc; } if (procSeq.length() <= 0) { return null; } return buildCompoundName(pkg, procSeq, "taskAffinity", outError); } private boolean parseKeySets(Package owner, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { // we've encountered the 'key-sets' tag // all the keys and keysets that we want must be defined here // so we're going to iterate over the parser and pull out the things we want int outerDepth = parser.getDepth(); int currentKeySetDepth = -1; int type; String currentKeySet = null; ArrayMap publicKeys = new ArrayMap(); ArraySet upgradeKeySets = new ArraySet(); ArrayMap> definedKeySets = new ArrayMap>(); ArraySet improperKeySets = new ArraySet(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG) { if (parser.getDepth() == currentKeySetDepth) { currentKeySet = null; currentKeySetDepth = -1; } continue; } String tagName = parser.getName(); if (tagName.equals("key-set")) { if (currentKeySet != null) { outError[0] = "Improperly nested 'key-set' tag at " + parser.getPositionDescription(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } final TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestKeySet); final String keysetName = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestKeySet_name); definedKeySets.put(keysetName, new ArraySet()); currentKeySet = keysetName; currentKeySetDepth = parser.getDepth(); sa.recycle(); } else if (tagName.equals("public-key")) { if (currentKeySet == null) { outError[0] = "Improperly nested 'key-set' tag at " + parser.getPositionDescription(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } final TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPublicKey); final String publicKeyName = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestPublicKey_name); final String encodedKey = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestPublicKey_value); if (encodedKey == null && publicKeys.get(publicKeyName) == null) { outError[0] = "'public-key' " + publicKeyName + " must define a public-key value" + " on first use at " + parser.getPositionDescription(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; sa.recycle(); return false; } else if (encodedKey != null) { PublicKey currentKey = parsePublicKey(encodedKey); if (currentKey == null) { Slog.w(TAG, "No recognized valid key in 'public-key' tag at " + parser.getPositionDescription() + " key-set " + currentKeySet + " will not be added to the package's defined key-sets."); sa.recycle(); improperKeySets.add(currentKeySet); XmlUtils.skipCurrentTag(parser); continue; } if (publicKeys.get(publicKeyName) == null || publicKeys.get(publicKeyName).equals(currentKey)) { /* public-key first definition, or matches old definition */ publicKeys.put(publicKeyName, currentKey); } else { outError[0] = "Value of 'public-key' " + publicKeyName + " conflicts with previously defined value at " + parser.getPositionDescription(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; sa.recycle(); return false; } } definedKeySets.get(currentKeySet).add(publicKeyName); sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("upgrade-key-set")) { final TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUpgradeKeySet); String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUpgradeKeySet_name); upgradeKeySets.add(name); sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (RIGID_PARSER) { outError[0] = "Bad element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } else { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } } Set publicKeyNames = publicKeys.keySet(); if (publicKeyNames.removeAll(definedKeySets.keySet())) { outError[0] = "Package" + owner.packageName + " AndroidManifext.xml " + "'key-set' and 'public-key' names must be distinct."; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.mKeySetMapping = new ArrayMap>(); for (ArrayMap.Entry> e: definedKeySets.entrySet()) { final String keySetName = e.getKey(); if (e.getValue().size() == 0) { Slog.w(TAG, "Package" + owner.packageName + " AndroidManifext.xml " + "'key-set' " + keySetName + " has no valid associated 'public-key'." + " Not including in package's defined key-sets."); continue; } else if (improperKeySets.contains(keySetName)) { Slog.w(TAG, "Package" + owner.packageName + " AndroidManifext.xml " + "'key-set' " + keySetName + " contained improper 'public-key'" + " tags. Not including in package's defined key-sets."); continue; } owner.mKeySetMapping.put(keySetName, new ArraySet()); for (String s : e.getValue()) { owner.mKeySetMapping.get(keySetName).add(publicKeys.get(s)); } } if (owner.mKeySetMapping.keySet().containsAll(upgradeKeySets)) { owner.mUpgradeKeySets = upgradeKeySets; } else { outError[0] ="Package" + owner.packageName + " AndroidManifext.xml " + "does not define all 'upgrade-key-set's ."; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } return true; } private boolean parsePermissionGroup(Package owner, int flags, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { PermissionGroup perm = new PermissionGroup(owner); TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPermissionGroup); if (!parsePackageItemInfo(owner, perm.info, outError, "", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermissionGroup_name, com.android.internal.R.styleable.AndroidManifestPermissionGroup_label, com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon, com.android.internal.R.styleable.AndroidManifestPermissionGroup_roundIcon, com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo, com.android.internal.R.styleable.AndroidManifestPermissionGroup_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } perm.info.descriptionRes = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestPermissionGroup_description, 0); perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0); perm.info.priority = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0); if (perm.info.priority > 0 && (flags&PARSE_IS_SYSTEM) == 0) { perm.info.priority = 0; } sa.recycle(); if (!parseAllMetaData(res, parser, "", perm, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.permissionGroups.add(perm); return true; } private boolean parsePermission(Package owner, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPermission); Permission perm = new Permission(owner); if (!parsePackageItemInfo(owner, perm.info, outError, "", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermission_name, com.android.internal.R.styleable.AndroidManifestPermission_label, com.android.internal.R.styleable.AndroidManifestPermission_icon, com.android.internal.R.styleable.AndroidManifestPermission_roundIcon, com.android.internal.R.styleable.AndroidManifestPermission_logo, com.android.internal.R.styleable.AndroidManifestPermission_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } // Note: don't allow this value to be a reference to a resource // that may change. perm.info.group = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup); if (perm.info.group != null) { perm.info.group = perm.info.group.intern(); } perm.info.descriptionRes = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestPermission_description, 0); perm.info.protectionLevel = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel, PermissionInfo.PROTECTION_NORMAL); perm.info.flags = sa.getInt( com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0); sa.recycle(); if (perm.info.protectionLevel == -1) { outError[0] = " does not specify protectionLevel"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel); if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) { if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_EPHEMERAL) == 0 && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0 && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) != PermissionInfo.PROTECTION_SIGNATURE) { outError[0] = " protectionLevel specifies a non-ephemeral flag but is " + "not based on signature type"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } } if (!parseAllMetaData(res, parser, "", perm, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.permissions.add(perm); return true; } private boolean parsePermissionTree(Package owner, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { Permission perm = new Permission(owner); TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPermissionTree); if (!parsePackageItemInfo(owner, perm.info, outError, "", sa, true /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestPermissionTree_name, com.android.internal.R.styleable.AndroidManifestPermissionTree_label, com.android.internal.R.styleable.AndroidManifestPermissionTree_icon, com.android.internal.R.styleable.AndroidManifestPermissionTree_roundIcon, com.android.internal.R.styleable.AndroidManifestPermissionTree_logo, com.android.internal.R.styleable.AndroidManifestPermissionTree_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } sa.recycle(); int index = perm.info.name.indexOf('.'); if (index > 0) { index = perm.info.name.indexOf('.', index+1); } if (index < 0) { outError[0] = " name has less than three segments: " + perm.info.name; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } perm.info.descriptionRes = 0; perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL; perm.tree = true; if (!parseAllMetaData(res, parser, "", perm, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.permissions.add(perm); return true; } private Instrumentation parseInstrumentation(Package owner, Resources res, XmlResourceParser parser, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestInstrumentation); if (mParseInstrumentationArgs == null) { mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestInstrumentation_name, com.android.internal.R.styleable.AndroidManifestInstrumentation_label, com.android.internal.R.styleable.AndroidManifestInstrumentation_icon, com.android.internal.R.styleable.AndroidManifestInstrumentation_roundIcon, com.android.internal.R.styleable.AndroidManifestInstrumentation_logo, com.android.internal.R.styleable.AndroidManifestInstrumentation_banner); mParseInstrumentationArgs.tag = ""; } mParseInstrumentationArgs.sa = sa; Instrumentation a = new Instrumentation(mParseInstrumentationArgs, new InstrumentationInfo()); if (outError[0] != null) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } String str; // Note: don't allow this value to be a reference to a resource // that may change. str = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage); a.info.targetPackage = str != null ? str.intern() : null; str = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestInstrumentation_targetProcesses); a.info.targetProcesses = str != null ? str.intern() : null; a.info.handleProfiling = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling, false); a.info.functionalTest = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestInstrumentation_functionalTest, false); sa.recycle(); if (a.info.targetPackage == null) { outError[0] = " does not specify targetPackage"; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } if (!parseAllMetaData(res, parser, "", a, outError)) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; } owner.instrumentation.add(a); return a; } /** * Parse the {@code application} XML tree at the current parse location in a * base APK manifest. *

* When adding new features, carefully consider if they should also be * supported by split APKs. */ private boolean parseBaseApplication(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final ApplicationInfo ai = owner.applicationInfo; final String pkgName = owner.applicationInfo.packageName; TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestApplication); if (!parsePackageItemInfo(owner, ai, outError, "", sa, false /*nameRequired*/, com.android.internal.R.styleable.AndroidManifestApplication_name, com.android.internal.R.styleable.AndroidManifestApplication_label, com.android.internal.R.styleable.AndroidManifestApplication_icon, com.android.internal.R.styleable.AndroidManifestApplication_roundIcon, com.android.internal.R.styleable.AndroidManifestApplication_logo, com.android.internal.R.styleable.AndroidManifestApplication_banner)) { sa.recycle(); mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } if (ai.name != null) { ai.className = ai.name; } String manageSpaceActivity = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity, Configuration.NATIVE_CONFIG_VERSION); if (manageSpaceActivity != null) { ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity, outError); } boolean allowBackup = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true); if (allowBackup) { ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; // backupAgent, killAfterRestore, fullBackupContent, backupInForeground, // and restoreAnyVersion are only relevant if backup is possible for the // given application. String backupAgent = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_backupAgent, Configuration.NATIVE_CONFIG_VERSION); if (backupAgent != null) { ai.backupAgentName = buildClassName(pkgName, backupAgent, outError); if (DEBUG_BACKUP) { Slog.v(TAG, "android:backupAgent = " + ai.backupAgentName + " from " + pkgName + "+" + backupAgent); } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore, true)) { ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion, false)) { ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly, false)) { ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_backupInForeground, false)) { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND; } } TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestApplication_fullBackupContent); if (v != null && (ai.fullBackupContent = v.resourceId) == 0) { if (DEBUG_BACKUP) { Slog.v(TAG, "fullBackupContent specified as boolean=" + (v.data == 0 ? "false" : "true")); } // "false" => -1, "true" => 0 ai.fullBackupContent = (v.data == 0 ? -1 : 0); } if (DEBUG_BACKUP) { Slog.v(TAG, "fullBackupContent=" + ai.fullBackupContent + " for " + pkgName); } } ai.theme = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_theme, 0); ai.descriptionRes = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_description, 0); if ((flags&PARSE_IS_SYSTEM) != 0) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_persistent, false)) { // Check if persistence is based on a feature being present final String requiredFeature = sa.getNonResourceString( com.android.internal.R.styleable. AndroidManifestApplication_persistentWhenFeatureAvailable); if (requiredFeature == null || mCallback.hasFeature(requiredFeature)) { ai.flags |= ApplicationInfo.FLAG_PERSISTENT; } } } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers, false)) { owner.mRequiredForAllUsers = true; } String restrictedAccountType = sa.getString(com.android.internal.R.styleable .AndroidManifestApplication_restrictedAccountType); if (restrictedAccountType != null && restrictedAccountType.length() > 0) { owner.mRestrictedAccountType = restrictedAccountType; } String requiredAccountType = sa.getString(com.android.internal.R.styleable .AndroidManifestApplication_requiredAccountType); if (requiredAccountType != null && requiredAccountType.length() > 0) { owner.mRequiredAccountType = requiredAccountType; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_debuggable, false)) { ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_vmSafeMode, false)) { ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE; } owner.baseHardwareAccelerated = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated, owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH); if (owner.baseHardwareAccelerated) { ai.flags |= ApplicationInfo.FLAG_HARDWARE_ACCELERATED; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_hasCode, true)) { ai.flags |= ApplicationInfo.FLAG_HAS_CODE; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting, false)) { ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData, true)) { ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA; } // The parent package controls installation, hence specify test only installs. if (owner.parentPackage == null) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_testOnly, false)) { ai.flags |= ApplicationInfo.FLAG_TEST_ONLY; } } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_largeHeap, false)) { ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic, true)) { ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl, false /* default is no RTL support*/)) { ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_multiArch, false)) { ai.flags |= ApplicationInfo.FLAG_MULTIARCH; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs, true)) { ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS; } if (sa.getBoolean( R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, false)) { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE; } if (sa.getBoolean( R.styleable.AndroidManifestApplication_directBootAware, false)) { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; } if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) { if (sa.getBoolean(R.styleable.AndroidManifestApplication_resizeableActivity, true)) { ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE; } else { ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; } } else if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N) { ai.privateFlags |= PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; } ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); ai.networkSecurityConfigRes = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig, 0); ai.category = sa.getInt( com.android.internal.R.styleable.AndroidManifestApplication_appCategory, ApplicationInfo.CATEGORY_UNDEFINED); String str; str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_permission, 0); ai.permission = (str != null && str.length() > 0) ? str.intern() : null; if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) { str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity, Configuration.NATIVE_CONFIG_VERSION); } else { // Some older apps have been seen to use a resource reference // here that on older builds was ignored (with a warning). We // need to continue to do this for them so they don't break. str = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity); } ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName, str, outError); if (outError[0] == null) { CharSequence pname; if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) { pname = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_process, Configuration.NATIVE_CONFIG_VERSION); } else { // Some older apps have been seen to use a resource reference // here that on older builds was ignored (with a warning). We // need to continue to do this for them so they don't break. pname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestApplication_process); } ai.processName = buildProcessName(ai.packageName, null, pname, flags, mSeparateProcesses, outError); ai.enabled = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_isGame, false)) { ai.flags |= ApplicationInfo.FLAG_IS_GAME; } if (false) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState, false)) { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE; // A heavy-weight application can not be in a custom process. // We can do direct compare because we intern all strings. if (ai.processName != null && ai.processName != ai.packageName) { outError[0] = "cantSaveState applications can not use custom processes"; } } } } ai.uiOptions = sa.getInt( com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0); sa.recycle(); if (outError[0] != null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } final int innerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String tagName = parser.getName(); if (tagName.equals("activity")) { Activity a = parseActivity(owner, res, parser, flags, outError, false, owner.baseHardwareAccelerated); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.activities.add(a); } else if (tagName.equals("receiver")) { Activity a = parseActivity(owner, res, parser, flags, outError, true, false); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.receivers.add(a); } else if (tagName.equals("service")) { Service s = parseService(owner, res, parser, flags, outError); if (s == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.services.add(s); } else if (tagName.equals("provider")) { Provider p = parseProvider(owner, res, parser, flags, outError); if (p == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.providers.add(p); } else if (tagName.equals("activity-alias")) { Activity a = parseActivityAlias(owner, res, parser, flags, outError); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.activities.add(a); } else if (parser.getName().equals("meta-data")) { // note: application meta-data is stored off to the side, so it can // remain null in the primary copy (we like to avoid extra copies because // it can be large) if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData, outError)) == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } } else if (tagName.equals("static-library")) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestStaticLibrary); // Note: don't allow this value to be a reference to a resource // that may change. final String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestStaticLibrary_name); final int version = sa.getInt( com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1); sa.recycle(); // Since the app canot run without a static lib - fail if malformed if (lname == null || version < 0) { outError[0] = "Bad static-library declaration name: " + lname + " version: " + version; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; XmlUtils.skipCurrentTag(parser); return false; } if (owner.mSharedUserId != null) { outError[0] = "sharedUserId not allowed in static shared library"; mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID; XmlUtils.skipCurrentTag(parser); return false; } if (owner.staticSharedLibName != null) { outError[0] = "Multiple static-shared libs for package " + pkgName; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; XmlUtils.skipCurrentTag(parser); return false; } owner.staticSharedLibName = lname.intern(); owner.staticSharedLibVersion = version; ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY; XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("library")) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestLibrary); // Note: don't allow this value to be a reference to a resource // that may change. String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestLibrary_name); sa.recycle(); if (lname != null) { lname = lname.intern(); if (!ArrayUtils.contains(owner.libraryNames, lname)) { owner.libraryNames = ArrayUtils.add( owner.libraryNames, lname); } } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-static-library")) { if (!parseUsesStaticLibrary(owner, res, parser, outError)) { return false; } } else if (tagName.equals("uses-library")) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesLibrary); // Note: don't allow this value to be a reference to a resource // that may change. String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); boolean req = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesLibrary_required, true); sa.recycle(); if (lname != null) { lname = lname.intern(); if (req) { owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname); } else { owner.usesOptionalLibraries = ArrayUtils.add( owner.usesOptionalLibraries, lname); } } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-package")) { // Dependencies for app installers; we don't currently try to // enforce this. XmlUtils.skipCurrentTag(parser); } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + tagName + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "Bad element under : " + tagName; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } } } modifySharedLibrariesForBackwardCompatibility(owner); if (hasDomainURLs(owner)) { owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS; } else { owner.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS; } return true; } private static void modifySharedLibrariesForBackwardCompatibility(Package owner) { // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need // to be an explicit dependency. // // A future change will remove this library from the boot classpath, at which point // all apps that target SDK 21 and earlier will have it automatically added to their // dependency lists. owner.usesLibraries = ArrayUtils.remove(owner.usesLibraries, "org.apache.http.legacy"); owner.usesOptionalLibraries = ArrayUtils.remove(owner.usesOptionalLibraries, "org.apache.http.legacy"); } /** * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI */ private static boolean hasDomainURLs(Package pkg) { if (pkg == null || pkg.activities == null) return false; final ArrayList activities = pkg.activities; final int countActivities = activities.size(); for (int n=0; n filters = activity.intents; if (filters == null) continue; final int countFilters = filters.size(); for (int m=0; msplit APK manifest. *

* Note that split APKs have many more restrictions on what they're capable * of doing, so many valid features of a base APK have been carefully * omitted here. */ private boolean parseSplitApplication(Package owner, Resources res, XmlResourceParser parser, int flags, int splitIndex, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestApplication); if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_hasCode, true)) { owner.splitFlags[splitIndex] |= ApplicationInfo.FLAG_HAS_CODE; } final int innerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } ComponentInfo parsedComponent = null; String tagName = parser.getName(); if (tagName.equals("activity")) { Activity a = parseActivity(owner, res, parser, flags, outError, false, owner.baseHardwareAccelerated); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.activities.add(a); parsedComponent = a.info; } else if (tagName.equals("receiver")) { Activity a = parseActivity(owner, res, parser, flags, outError, true, false); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.receivers.add(a); parsedComponent = a.info; } else if (tagName.equals("service")) { Service s = parseService(owner, res, parser, flags, outError); if (s == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.services.add(s); parsedComponent = s.info; } else if (tagName.equals("provider")) { Provider p = parseProvider(owner, res, parser, flags, outError); if (p == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.providers.add(p); parsedComponent = p.info; } else if (tagName.equals("activity-alias")) { Activity a = parseActivityAlias(owner, res, parser, flags, outError); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.activities.add(a); parsedComponent = a.info; } else if (parser.getName().equals("meta-data")) { // note: application meta-data is stored off to the side, so it can // remain null in the primary copy (we like to avoid extra copies because // it can be large) if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData, outError)) == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } } else if (tagName.equals("uses-static-library")) { if (!parseUsesStaticLibrary(owner, res, parser, outError)) { return false; } } else if (tagName.equals("uses-library")) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestUsesLibrary); // Note: don't allow this value to be a reference to a resource // that may change. String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); boolean req = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestUsesLibrary_required, true); sa.recycle(); if (lname != null) { lname = lname.intern(); if (req) { // Upgrade to treat as stronger constraint owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname); owner.usesOptionalLibraries = ArrayUtils.remove( owner.usesOptionalLibraries, lname); } else { // Ignore if someone already defined as required if (!ArrayUtils.contains(owner.usesLibraries, lname)) { owner.usesOptionalLibraries = ArrayUtils.add( owner.usesOptionalLibraries, lname); } } } XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("uses-package")) { // Dependencies for app installers; we don't currently try to // enforce this. XmlUtils.skipCurrentTag(parser); } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + tagName + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "Bad element under : " + tagName; mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } } if (parsedComponent != null && parsedComponent.splitName == null) { // If the loaded component did not specify a split, inherit the split name // based on the split it is defined in. // This is used to later load the correct split when starting this // component. parsedComponent.splitName = owner.splitNames[splitIndex]; } } return true; } private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, String[] outError, String tag, TypedArray sa, boolean nameRequired, int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) { // This case can only happen in unit tests where we sometimes need to create fakes // of various package parser data structures. if (sa == null) { outError[0] = tag + " does not contain any attributes"; return false; } String name = sa.getNonConfigurationString(nameRes, 0); if (name == null) { if (nameRequired) { outError[0] = tag + " does not specify android:name"; return false; } } else { outInfo.name = buildClassName(owner.applicationInfo.packageName, name, outError); if (outInfo.name == null) { return false; } } final boolean useRoundIcon = Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon); int roundIconVal = useRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0; if (roundIconVal != 0) { outInfo.icon = roundIconVal; outInfo.nonLocalizedLabel = null; } else { int iconVal = sa.getResourceId(iconRes, 0); if (iconVal != 0) { outInfo.icon = iconVal; outInfo.nonLocalizedLabel = null; } } int logoVal = sa.getResourceId(logoRes, 0); if (logoVal != 0) { outInfo.logo = logoVal; } int bannerVal = sa.getResourceId(bannerRes, 0); if (bannerVal != 0) { outInfo.banner = bannerVal; } TypedValue v = sa.peekValue(labelRes); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { outInfo.nonLocalizedLabel = v.coerceToString(); } outInfo.packageName = owner.packageName; return true; } private Activity parseActivity(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError, boolean receiver, boolean hardwareAccelerated) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity); if (mParseActivityArgs == null) { mParseActivityArgs = new ParseComponentArgs(owner, outError, R.styleable.AndroidManifestActivity_name, R.styleable.AndroidManifestActivity_label, R.styleable.AndroidManifestActivity_icon, R.styleable.AndroidManifestActivity_roundIcon, R.styleable.AndroidManifestActivity_logo, R.styleable.AndroidManifestActivity_banner, mSeparateProcesses, R.styleable.AndroidManifestActivity_process, R.styleable.AndroidManifestActivity_description, R.styleable.AndroidManifestActivity_enabled); } mParseActivityArgs.tag = receiver ? "" : ""; mParseActivityArgs.sa = sa; mParseActivityArgs.flags = flags; Activity a = new Activity(mParseActivityArgs, new ActivityInfo()); if (outError[0] != null) { sa.recycle(); return null; } boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported); if (setExported) { a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false); } a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0); a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, a.info.applicationInfo.uiOptions); String parentName = sa.getNonConfigurationString( R.styleable.AndroidManifestActivity_parentActivityName, Configuration.NATIVE_CONFIG_VERSION); if (parentName != null) { String parentClassName = buildClassName(a.info.packageName, parentName, outError); if (outError[0] == null) { a.info.parentActivityName = parentClassName; } else { Log.e(TAG, "Activity " + a.info.name + " specified invalid parentActivityName " + parentName); outError[0] = null; } } String str; str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0); if (str == null) { a.info.permission = owner.applicationInfo.permission; } else { a.info.permission = str.length() > 0 ? str.toString().intern() : null; } str = sa.getNonConfigurationString( R.styleable.AndroidManifestActivity_taskAffinity, Configuration.NATIVE_CONFIG_VERSION); a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName, owner.applicationInfo.taskAffinity, str, outError); a.info.splitName = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0); a.info.flags = 0; if (sa.getBoolean( R.styleable.AndroidManifestActivity_multiprocess, false)) { a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) { a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) { a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) { a.info.flags |= ActivityInfo.FLAG_NO_HISTORY; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) { a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) { a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) { a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting, (owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) { a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, false)) { a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false) || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) { a.info.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) { a.info.flags |= ActivityInfo.FLAG_IMMERSIVE; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) { a.info.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY; } if (!receiver) { if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated, hardwareAccelerated)) { a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED; } a.info.launchMode = sa.getInt( R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE); a.info.documentLaunchMode = sa.getInt( R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE); a.info.maxRecents = sa.getInt( R.styleable.AndroidManifestActivity_maxRecents, ActivityManager.getDefaultAppRecentsLimitStatic()); a.info.configChanges = getActivityConfigChanges( sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0), sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0)); a.info.softInputMode = sa.getInt( R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); a.info.persistableMode = sa.getInteger( R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY); if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) { a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents, false)) { a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity, false)) { a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) { a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING; } a.info.screenOrientation = sa.getInt( R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED); setActivityResizeMode(a.info, sa, owner); if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture, false)) { a.info.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE; } if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) { a.info.flags |= FLAG_ALWAYS_FOCUSABLE; } setActivityMaxAspectRatio(a.info, sa, owner); a.info.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0); a.info.encryptionAware = a.info.directBootAware = sa.getBoolean( R.styleable.AndroidManifestActivity_directBootAware, false); a.info.requestedVrComponent = sa.getString(R.styleable.AndroidManifestActivity_enableVrMode); a.info.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, ROTATION_ANIMATION_ROTATE); a.info.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT); } else { a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; a.info.configChanges = 0; if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) { a.info.flags |= ActivityInfo.FLAG_SINGLE_USER; if (a.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Activity exported request ignored due to singleUser: " + a.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); a.info.exported = false; setExported = true; } } a.info.encryptionAware = a.info.directBootAware = sa.getBoolean( R.styleable.AndroidManifestActivity_directBootAware, false); } if (a.info.directBootAware) { owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE; } // can't make this final; we may set it later via meta-data boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false); if (visibleToEphemeral) { a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; owner.visibleToInstantApps = true; } sa.recycle(); if (receiver && (owner.applicationInfo.privateFlags &ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { // A heavy-weight application can not have receives in its main process // We can do direct compare because we intern all strings. if (a.info.processName == owner.packageName) { outError[0] = "Heavy-weight applications can not have receivers in main process"; } } if (outError[0] != null) { return null; } int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (parser.getName().equals("intent-filter")) { ActivityIntentInfo intent = new ActivityIntentInfo(a); if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, intent, outError)) { return null; } if (intent.countActions() == 0) { Slog.w(TAG, "No actions in intent filter at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { a.intents.add(intent); } // adjust activity flags when we implicitly expose it via a browsable filter final int visibility = visibleToEphemeral ? IntentFilter.VISIBILITY_EXPLICIT : !receiver && isImplicitlyExposedIntent(intent) ? IntentFilter.VISIBILITY_IMPLICIT : IntentFilter.VISIBILITY_NONE; intent.setVisibilityToInstantApp(visibility); if (intent.isVisibleToInstantApp()) { a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; } if (intent.isImplicitlyVisibleToInstantApp()) { a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; } if (LOG_UNSAFE_BROADCASTS && receiver && (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O)) { for (int i = 0; i < intent.countActions(); i++) { final String action = intent.getAction(i); if (action == null || !action.startsWith("android.")) continue; if (!SAFE_BROADCASTS.contains(action)) { Slog.w(TAG, "Broadcast " + action + " may never be delivered to " + owner.packageName + " as requested at: " + parser.getPositionDescription()); } } } } else if (!receiver && parser.getName().equals("preferred")) { ActivityIntentInfo intent = new ActivityIntentInfo(a); if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/, intent, outError)) { return null; } if (intent.countActions() == 0) { Slog.w(TAG, "No actions in preferred at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { if (owner.preferredActivityFilters == null) { owner.preferredActivityFilters = new ArrayList(); } owner.preferredActivityFilters.add(intent); } // adjust activity flags when we implicitly expose it via a browsable filter final int visibility = visibleToEphemeral ? IntentFilter.VISIBILITY_EXPLICIT : !receiver && isImplicitlyExposedIntent(intent) ? IntentFilter.VISIBILITY_IMPLICIT : IntentFilter.VISIBILITY_NONE; intent.setVisibilityToInstantApp(visibility); if (intent.isVisibleToInstantApp()) { a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; } if (intent.isImplicitlyVisibleToInstantApp()) { a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; } } else if (parser.getName().equals("meta-data")) { if ((a.metaData = parseMetaData(res, parser, a.metaData, outError)) == null) { return null; } // we don't have an attribute [or it's false], but, we have meta-data if (!visibleToEphemeral && a.metaData.getBoolean(META_DATA_INSTANT_APPS)) { visibleToEphemeral = true; // set in case there are more intent filters a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; a.info.flags &= ~ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; owner.visibleToInstantApps = true; // cycle through any filters already seen for (int i = a.intents.size() - 1; i >= 0; --i) { a.intents.get(i) .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); } if (owner.preferredActivityFilters != null) { for (int i = owner.preferredActivityFilters.size() - 1; i >= 0; --i) { owner.preferredActivityFilters.get(i) .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); } } } } else if (!receiver && parser.getName().equals("layout")) { parseLayout(res, parser, a); } else { if (!RIGID_PARSER) { Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); if (receiver) { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } XmlUtils.skipCurrentTag(parser); continue; } else { if (receiver) { outError[0] = "Bad element under : " + parser.getName(); } else { outError[0] = "Bad element under : " + parser.getName(); } return null; } } } if (!setExported) { a.info.exported = a.intents.size() > 0; } return a; } private void setActivityResizeMode(ActivityInfo aInfo, TypedArray sa, Package owner) { final boolean appExplicitDefault = (owner.applicationInfo.privateFlags & (PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE | PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0; if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity) || appExplicitDefault) { // Activity or app explicitly set if it is resizeable or not; final boolean appResizeable = (owner.applicationInfo.privateFlags & PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0; if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, appResizeable)) { aInfo.resizeMode = RESIZE_MODE_RESIZEABLE; } else { aInfo.resizeMode = RESIZE_MODE_UNRESIZEABLE; } return; } if ((owner.applicationInfo.privateFlags & PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) != 0) { // The activity or app didn't explicitly set the resizing option, however we want to // make it resize due to the sdk version it is targeting. aInfo.resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; return; } // resize preference isn't set and target sdk version doesn't support resizing apps by // default. For the app to be resizeable if it isn't fixed orientation or immersive. if (aInfo.isFixedOrientationPortrait()) { aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; } else if (aInfo.isFixedOrientationLandscape()) { aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; } else if (aInfo.isFixedOrientation()) { aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; } else { aInfo.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE; } } private void setActivityMaxAspectRatio(ActivityInfo aInfo, TypedArray sa, Package owner) { if (aInfo.resizeMode == RESIZE_MODE_RESIZEABLE || aInfo.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) { // Resizeable activities can be put in any aspect ratio. aInfo.maxAspectRatio = 0; return; } // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater. // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD. float defaultMaxAspectRatio = owner.applicationInfo.targetSdkVersion < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; if (owner.applicationInfo.maxAspectRatio != 0 ) { // Use the application max aspect ration as default if set. defaultMaxAspectRatio = owner.applicationInfo.maxAspectRatio; } aInfo.maxAspectRatio = sa.getFloat( R.styleable.AndroidManifestActivity_maxAspectRatio, defaultMaxAspectRatio); if (aInfo.maxAspectRatio < 1.0f && aInfo.maxAspectRatio != 0) { // Ignore any value lesser than 1.0. aInfo.maxAspectRatio = 0; } } /** * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml. * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from * AndroidManifest.xml. * @hide Exposed for unit testing only. */ @TestApi public static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) { return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK); } private void parseLayout(Resources res, AttributeSet attrs, Activity a) { TypedArray sw = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestLayout); int width = -1; float widthFraction = -1f; int height = -1; float heightFraction = -1f; final int widthType = sw.getType( com.android.internal.R.styleable.AndroidManifestLayout_defaultWidth); if (widthType == TypedValue.TYPE_FRACTION) { widthFraction = sw.getFraction( com.android.internal.R.styleable.AndroidManifestLayout_defaultWidth, 1, 1, -1); } else if (widthType == TypedValue.TYPE_DIMENSION) { width = sw.getDimensionPixelSize( com.android.internal.R.styleable.AndroidManifestLayout_defaultWidth, -1); } final int heightType = sw.getType( com.android.internal.R.styleable.AndroidManifestLayout_defaultHeight); if (heightType == TypedValue.TYPE_FRACTION) { heightFraction = sw.getFraction( com.android.internal.R.styleable.AndroidManifestLayout_defaultHeight, 1, 1, -1); } else if (heightType == TypedValue.TYPE_DIMENSION) { height = sw.getDimensionPixelSize( com.android.internal.R.styleable.AndroidManifestLayout_defaultHeight, -1); } int gravity = sw.getInt( com.android.internal.R.styleable.AndroidManifestLayout_gravity, Gravity.CENTER); int minWidth = sw.getDimensionPixelSize( com.android.internal.R.styleable.AndroidManifestLayout_minWidth, -1); int minHeight = sw.getDimensionPixelSize( com.android.internal.R.styleable.AndroidManifestLayout_minHeight, -1); sw.recycle(); a.info.windowLayout = new ActivityInfo.WindowLayout(width, widthFraction, height, heightFraction, gravity, minWidth, minHeight); } private Activity parseActivityAlias(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestActivityAlias); String targetActivity = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity, Configuration.NATIVE_CONFIG_VERSION); if (targetActivity == null) { outError[0] = " does not specify android:targetActivity"; sa.recycle(); return null; } targetActivity = buildClassName(owner.applicationInfo.packageName, targetActivity, outError); if (targetActivity == null) { sa.recycle(); return null; } if (mParseActivityAliasArgs == null) { mParseActivityAliasArgs = new ParseComponentArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestActivityAlias_name, com.android.internal.R.styleable.AndroidManifestActivityAlias_label, com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, com.android.internal.R.styleable.AndroidManifestActivityAlias_roundIcon, com.android.internal.R.styleable.AndroidManifestActivityAlias_logo, com.android.internal.R.styleable.AndroidManifestActivityAlias_banner, mSeparateProcesses, 0, com.android.internal.R.styleable.AndroidManifestActivityAlias_description, com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled); mParseActivityAliasArgs.tag = ""; } mParseActivityAliasArgs.sa = sa; mParseActivityAliasArgs.flags = flags; Activity target = null; final int NA = owner.activities.size(); for (int i=0; i 0 ? str.toString().intern() : null; } String parentName = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestActivityAlias_parentActivityName, Configuration.NATIVE_CONFIG_VERSION); if (parentName != null) { String parentClassName = buildClassName(a.info.packageName, parentName, outError); if (outError[0] == null) { a.info.parentActivityName = parentClassName; } else { Log.e(TAG, "Activity alias " + a.info.name + " specified invalid parentActivityName " + parentName); outError[0] = null; } } // TODO add visibleToInstantApps attribute to activity alias final boolean visibleToEphemeral = ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0); sa.recycle(); if (outError[0] != null) { return null; } int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (parser.getName().equals("intent-filter")) { ActivityIntentInfo intent = new ActivityIntentInfo(a); if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, intent, outError)) { return null; } if (intent.countActions() == 0) { Slog.w(TAG, "No actions in intent filter at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { a.intents.add(intent); } // adjust activity flags when we implicitly expose it via a browsable filter final int visibility = visibleToEphemeral ? IntentFilter.VISIBILITY_EXPLICIT : isImplicitlyExposedIntent(intent) ? IntentFilter.VISIBILITY_IMPLICIT : IntentFilter.VISIBILITY_NONE; intent.setVisibilityToInstantApp(visibility); if (intent.isVisibleToInstantApp()) { a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; } if (intent.isImplicitlyVisibleToInstantApp()) { a.info.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; } } else if (parser.getName().equals("meta-data")) { if ((a.metaData=parseMetaData(res, parser, a.metaData, outError)) == null) { return null; } } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "Bad element under : " + parser.getName(); return null; } } } if (!setExported) { a.info.exported = a.intents.size() > 0; } return a; } private Provider parseProvider(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestProvider); if (mParseProviderArgs == null) { mParseProviderArgs = new ParseComponentArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestProvider_name, com.android.internal.R.styleable.AndroidManifestProvider_label, com.android.internal.R.styleable.AndroidManifestProvider_icon, com.android.internal.R.styleable.AndroidManifestProvider_roundIcon, com.android.internal.R.styleable.AndroidManifestProvider_logo, com.android.internal.R.styleable.AndroidManifestProvider_banner, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestProvider_process, com.android.internal.R.styleable.AndroidManifestProvider_description, com.android.internal.R.styleable.AndroidManifestProvider_enabled); mParseProviderArgs.tag = ""; } mParseProviderArgs.sa = sa; mParseProviderArgs.flags = flags; Provider p = new Provider(mParseProviderArgs, new ProviderInfo()); if (outError[0] != null) { sa.recycle(); return null; } boolean providerExportedDefault = false; if (owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) { // For compatibility, applications targeting API level 16 or lower // should have their content providers exported by default, unless they // specify otherwise. providerExportedDefault = true; } p.info.exported = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_exported, providerExportedDefault); String cpname = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestProvider_authorities, 0); p.info.isSyncable = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_syncable, false); String permission = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestProvider_permission, 0); String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestProvider_readPermission, 0); if (str == null) { str = permission; } if (str == null) { p.info.readPermission = owner.applicationInfo.permission; } else { p.info.readPermission = str.length() > 0 ? str.toString().intern() : null; } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestProvider_writePermission, 0); if (str == null) { str = permission; } if (str == null) { p.info.writePermission = owner.applicationInfo.permission; } else { p.info.writePermission = str.length() > 0 ? str.toString().intern() : null; } p.info.grantUriPermissions = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_grantUriPermissions, false); p.info.multiprocess = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_multiprocess, false); p.info.initOrder = sa.getInt( com.android.internal.R.styleable.AndroidManifestProvider_initOrder, 0); p.info.splitName = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0); p.info.flags = 0; if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_singleUser, false)) { p.info.flags |= ProviderInfo.FLAG_SINGLE_USER; if (p.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Provider exported request ignored due to singleUser: " + p.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); p.info.exported = false; } } p.info.encryptionAware = p.info.directBootAware = sa.getBoolean( R.styleable.AndroidManifestProvider_directBootAware, false); if (p.info.directBootAware) { owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE; } final boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false); if (visibleToEphemeral) { p.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; owner.visibleToInstantApps = true; } sa.recycle(); if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { // A heavy-weight application can not have providers in its main process // We can do direct compare because we intern all strings. if (p.info.processName == owner.packageName) { outError[0] = "Heavy-weight applications can not have providers in main process"; return null; } } if (cpname == null) { outError[0] = " does not include authorities attribute"; return null; } if (cpname.length() <= 0) { outError[0] = " has empty authorities attribute"; return null; } p.info.authority = cpname.intern(); if (!parseProviderTags( res, parser, visibleToEphemeral, owner, p, outError)) { return null; } return p; } private boolean parseProviderTags(Resources res, XmlResourceParser parser, boolean visibleToEphemeral, Package owner, Provider outInfo, String[] outError) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (parser.getName().equals("intent-filter")) { ProviderIntentInfo intent = new ProviderIntentInfo(outInfo); if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/, intent, outError)) { return false; } if (visibleToEphemeral) { intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; } outInfo.intents.add(intent); } else if (parser.getName().equals("meta-data")) { if ((outInfo.metaData=parseMetaData(res, parser, outInfo.metaData, outError)) == null) { return false; } // we don't have an attribute [or it's false], but, we have meta-data if (!visibleToEphemeral && outInfo.metaData.getBoolean(META_DATA_INSTANT_APPS)) { visibleToEphemeral = true; // set in case there are more intent filters outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; owner.visibleToInstantApps = true; // cycle through any filters already seen for (int i = outInfo.intents.size() - 1; i >= 0; --i) { outInfo.intents.get(i) .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); } } } else if (parser.getName().equals("grant-uri-permission")) { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestGrantUriPermission); PatternMatcher pa = null; String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } sa.recycle(); if (pa != null) { if (outInfo.info.uriPermissionPatterns == null) { outInfo.info.uriPermissionPatterns = new PatternMatcher[1]; outInfo.info.uriPermissionPatterns[0] = pa; } else { final int N = outInfo.info.uriPermissionPatterns.length; PatternMatcher[] newp = new PatternMatcher[N+1]; System.arraycopy(outInfo.info.uriPermissionPatterns, 0, newp, 0, N); newp[N] = pa; outInfo.info.uriPermissionPatterns = newp; } outInfo.info.grantUriPermissions = true; } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "No path, pathPrefix, or pathPattern for "; return false; } } XmlUtils.skipCurrentTag(parser); } else if (parser.getName().equals("path-permission")) { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestPathPermission); PathPermission pa = null; String permission = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_permission, 0); String readPermission = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission, 0); if (readPermission == null) { readPermission = permission; } String writePermission = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission, 0); if (writePermission == null) { writePermission = permission; } boolean havePerm = false; if (readPermission != null) { readPermission = readPermission.intern(); havePerm = true; } if (writePermission != null) { writePermission = writePermission.intern(); havePerm = true; } if (!havePerm) { if (!RIGID_PARSER) { Slog.w(TAG, "No readPermission or writePermssion for : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "No readPermission or writePermssion for "; return false; } } String path = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_path, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL, readPermission, writePermission); } path = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission, writePermission); } path = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission); } path = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission); } sa.recycle(); if (pa != null) { if (outInfo.info.pathPermissions == null) { outInfo.info.pathPermissions = new PathPermission[1]; outInfo.info.pathPermissions[0] = pa; } else { final int N = outInfo.info.pathPermissions.length; PathPermission[] newp = new PathPermission[N+1]; System.arraycopy(outInfo.info.pathPermissions, 0, newp, 0, N); newp[N] = pa; outInfo.info.pathPermissions = newp; } } else { if (!RIGID_PARSER) { Slog.w(TAG, "No path, pathPrefix, or pathPattern for : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } outError[0] = "No path, pathPrefix, or pathPattern for "; return false; } XmlUtils.skipCurrentTag(parser); } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "Bad element under : " + parser.getName(); return false; } } } return true; } private Service parseService(Package owner, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestService); if (mParseServiceArgs == null) { mParseServiceArgs = new ParseComponentArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestService_name, com.android.internal.R.styleable.AndroidManifestService_label, com.android.internal.R.styleable.AndroidManifestService_icon, com.android.internal.R.styleable.AndroidManifestService_roundIcon, com.android.internal.R.styleable.AndroidManifestService_logo, com.android.internal.R.styleable.AndroidManifestService_banner, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestService_process, com.android.internal.R.styleable.AndroidManifestService_description, com.android.internal.R.styleable.AndroidManifestService_enabled); mParseServiceArgs.tag = ""; } mParseServiceArgs.sa = sa; mParseServiceArgs.flags = flags; Service s = new Service(mParseServiceArgs, new ServiceInfo()); if (outError[0] != null) { sa.recycle(); return null; } boolean setExported = sa.hasValue( com.android.internal.R.styleable.AndroidManifestService_exported); if (setExported) { s.info.exported = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestService_exported, false); } String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestService_permission, 0); if (str == null) { s.info.permission = owner.applicationInfo.permission; } else { s.info.permission = str.length() > 0 ? str.toString().intern() : null; } s.info.splitName = sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0); s.info.flags = 0; if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestService_stopWithTask, false)) { s.info.flags |= ServiceInfo.FLAG_STOP_WITH_TASK; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestService_isolatedProcess, false)) { s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestService_externalService, false)) { s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE; } if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestService_singleUser, false)) { s.info.flags |= ServiceInfo.FLAG_SINGLE_USER; if (s.info.exported && (flags & PARSE_IS_PRIVILEGED) == 0) { Slog.w(TAG, "Service exported request ignored due to singleUser: " + s.className + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); s.info.exported = false; setExported = true; } } s.info.encryptionAware = s.info.directBootAware = sa.getBoolean( R.styleable.AndroidManifestService_directBootAware, false); if (s.info.directBootAware) { owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE; } boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestService_visibleToInstantApps, false); if (visibleToEphemeral) { s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP; owner.visibleToInstantApps = true; } sa.recycle(); if ((owner.applicationInfo.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { // A heavy-weight application can not have services in its main process // We can do direct compare because we intern all strings. if (s.info.processName == owner.packageName) { outError[0] = "Heavy-weight applications can not have services in main process"; return null; } } int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (parser.getName().equals("intent-filter")) { ServiceIntentInfo intent = new ServiceIntentInfo(s); if (!parseIntent(res, parser, true /*allowGlobs*/, false /*allowAutoVerify*/, intent, outError)) { return null; } if (visibleToEphemeral) { intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP; } s.intents.add(intent); } else if (parser.getName().equals("meta-data")) { if ((s.metaData=parseMetaData(res, parser, s.metaData, outError)) == null) { return null; } // we don't have an attribute [or it's false], but, we have meta-data if (!visibleToEphemeral && s.metaData.getBoolean(META_DATA_INSTANT_APPS)) { visibleToEphemeral = true; // set in case there are more intent filters s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP; owner.visibleToInstantApps = true; // cycle through any filters already seen for (int i = s.intents.size() - 1; i >= 0; --i) { s.intents.get(i) .setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT); } } } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "Bad element under : " + parser.getName(); return null; } } } if (!setExported) { s.info.exported = s.intents.size() > 0; } return s; } private boolean isImplicitlyExposedIntent(IntentInfo intent) { return intent.hasCategory(Intent.CATEGORY_BROWSABLE) || intent.hasAction(Intent.ACTION_SEND) || intent.hasAction(Intent.ACTION_SENDTO) || intent.hasAction(Intent.ACTION_SEND_MULTIPLE); } private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag, Component outInfo, String[] outError) throws XmlPullParserException, IOException { int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } if (parser.getName().equals("meta-data")) { if ((outInfo.metaData=parseMetaData(res, parser, outInfo.metaData, outError)) == null) { return false; } } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under " + tag + ": " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); continue; } else { outError[0] = "Bad element under " + tag + ": " + parser.getName(); return false; } } } return true; } private Bundle parseMetaData(Resources res, XmlResourceParser parser, Bundle data, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestMetaData); if (data == null) { data = new Bundle(); } String name = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestMetaData_name, 0); if (name == null) { outError[0] = " requires an android:name attribute"; sa.recycle(); return null; } name = name.intern(); TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestMetaData_resource); if (v != null && v.resourceId != 0) { //Slog.i(TAG, "Meta data ref " + name + ": " + v); data.putInt(name, v.resourceId); } else { v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestMetaData_value); //Slog.i(TAG, "Meta data " + name + ": " + v); if (v != null) { if (v.type == TypedValue.TYPE_STRING) { CharSequence cs = v.coerceToString(); data.putString(name, cs != null ? cs.toString() : null); } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { data.putBoolean(name, v.data != 0); } else if (v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT) { data.putInt(name, v.data); } else if (v.type == TypedValue.TYPE_FLOAT) { data.putFloat(name, v.getFloat()); } else { if (!RIGID_PARSER) { Slog.w(TAG, " only supports string, integer, float, color, boolean, and resource reference types: " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); } else { outError[0] = " only supports string, integer, float, color, boolean, and resource reference types"; data = null; } } } else { outError[0] = " requires an android:value or android:resource attribute"; data = null; } } sa.recycle(); XmlUtils.skipCurrentTag(parser); return data; } private static VerifierInfo parseVerifier(AttributeSet attrs) { String packageName = null; String encodedPublicKey = null; final int attrCount = attrs.getAttributeCount(); for (int i = 0; i < attrCount; i++) { final int attrResId = attrs.getAttributeNameResource(i); switch (attrResId) { case com.android.internal.R.attr.name: packageName = attrs.getAttributeValue(i); break; case com.android.internal.R.attr.publicKey: encodedPublicKey = attrs.getAttributeValue(i); break; } } if (packageName == null || packageName.length() == 0) { Slog.i(TAG, "verifier package name was null; skipping"); return null; } final PublicKey publicKey = parsePublicKey(encodedPublicKey); if (publicKey == null) { Slog.i(TAG, "Unable to parse verifier public key for " + packageName); return null; } return new VerifierInfo(packageName, publicKey); } public static final PublicKey parsePublicKey(final String encodedPublicKey) { if (encodedPublicKey == null) { Slog.w(TAG, "Could not parse null public key"); return null; } EncodedKeySpec keySpec; try { final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT); keySpec = new X509EncodedKeySpec(encoded); } catch (IllegalArgumentException e) { Slog.w(TAG, "Could not parse verifier public key; invalid Base64"); return null; } /* First try the key as an RSA key. */ try { final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { Slog.wtf(TAG, "Could not parse public key: RSA KeyFactory not included in build"); } catch (InvalidKeySpecException e) { // Not a RSA public key. } /* Now try it as a ECDSA key. */ try { final KeyFactory keyFactory = KeyFactory.getInstance("EC"); return keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { Slog.wtf(TAG, "Could not parse public key: EC KeyFactory not included in build"); } catch (InvalidKeySpecException e) { // Not a ECDSA public key. } /* Now try it as a DSA key. */ try { final KeyFactory keyFactory = KeyFactory.getInstance("DSA"); return keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { Slog.wtf(TAG, "Could not parse public key: DSA KeyFactory not included in build"); } catch (InvalidKeySpecException e) { // Not a DSA public key. } /* Not a supported key type */ return null; } private static final String ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android"; private boolean parseIntent(Resources res, XmlResourceParser parser, boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestIntentFilter); int priority = sa.getInt( com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0); outInfo.setPriority(priority); TypedValue v = sa.peekValue( com.android.internal.R.styleable.AndroidManifestIntentFilter_label); if (v != null && (outInfo.labelRes=v.resourceId) == 0) { outInfo.nonLocalizedLabel = v.coerceToString(); } final boolean useRoundIcon = Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon); int roundIconVal = useRoundIcon ? sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0; if (roundIconVal != 0) { outInfo.icon = roundIconVal; } else { outInfo.icon = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_icon, 0); } outInfo.logo = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_logo, 0); outInfo.banner = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestIntentFilter_banner, 0); if (allowAutoVerify) { outInfo.setAutoVerify(sa.getBoolean( com.android.internal.R.styleable.AndroidManifestIntentFilter_autoVerify, false)); } sa.recycle(); int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { continue; } String nodeName = parser.getName(); if (nodeName.equals("action")) { String value = parser.getAttributeValue( ANDROID_RESOURCES, "name"); if (value == null || value == "") { outError[0] = "No value supplied for "; return false; } XmlUtils.skipCurrentTag(parser); outInfo.addAction(value); } else if (nodeName.equals("category")) { String value = parser.getAttributeValue( ANDROID_RESOURCES, "name"); if (value == null || value == "") { outError[0] = "No value supplied for "; return false; } XmlUtils.skipCurrentTag(parser); outInfo.addCategory(value); } else if (nodeName.equals("data")) { sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestData); String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_mimeType, 0); if (str != null) { try { outInfo.addDataType(str); } catch (IntentFilter.MalformedMimeTypeException e) { outError[0] = e.toString(); sa.recycle(); return false; } } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_scheme, 0); if (str != null) { outInfo.addDataScheme(str); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_ssp, 0); if (str != null) { outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_sspPrefix, 0); if (str != null) { outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_sspPattern, 0); if (str != null) { if (!allowGlobs) { outError[0] = "sspPattern not allowed here; ssp must be literal"; return false; } outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } String host = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_host, 0); String port = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_port, 0); if (host != null) { outInfo.addDataAuthority(host, port); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_path, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_pathPrefix, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_pathPattern, 0); if (str != null) { if (!allowGlobs) { outError[0] = "pathPattern not allowed here; path must be literal"; return false; } outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_pathAdvancedPattern, 0); if (str != null) { if (!allowGlobs) { outError[0] = "pathAdvancedPattern not allowed here; path must be literal"; return false; } outInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB); } sa.recycle(); XmlUtils.skipCurrentTag(parser); } else if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under : " + parser.getName() + " at " + mArchiveSourcePath + " " + parser.getPositionDescription()); XmlUtils.skipCurrentTag(parser); } else { outError[0] = "Bad element under : " + parser.getName(); return false; } } outInfo.hasDefault = outInfo.hasCategory(Intent.CATEGORY_DEFAULT); if (DEBUG_PARSER) { final StringBuilder cats = new StringBuilder("Intent d="); cats.append(outInfo.hasDefault); cats.append(", cat="); final Iterator it = outInfo.categoriesIterator(); if (it != null) { while (it.hasNext()) { cats.append(' '); cats.append(it.next()); } } Slog.d(TAG, cats.toString()); } return true; } /** * Representation of a full package parsed from APK files on disk. A package * consists of a single base APK, and zero or more split APKs. */ public final static class Package implements Parcelable { public String packageName; // The package name declared in the manifest as the package can be // renamed, for example static shared libs use synthetic package names. public String manifestPackageName; /** Names of any split APKs, ordered by parsed splitName */ public String[] splitNames; // TODO: work towards making these paths invariant public String volumeUuid; /** * Path where this package was found on disk. For monolithic packages * this is path to single base APK file; for cluster packages this is * path to the cluster directory. */ public String codePath; /** Path of base APK */ public String baseCodePath; /** Paths of any split APKs, ordered by parsed splitName */ public String[] splitCodePaths; /** Revision code of base APK */ public int baseRevisionCode; /** Revision codes of any split APKs, ordered by parsed splitName */ public int[] splitRevisionCodes; /** Flags of any split APKs; ordered by parsed splitName */ public int[] splitFlags; /** * Private flags of any split APKs; ordered by parsed splitName. * * {@hide} */ public int[] splitPrivateFlags; public boolean baseHardwareAccelerated; // For now we only support one application per package. public ApplicationInfo applicationInfo = new ApplicationInfo(); public final ArrayList permissions = new ArrayList(0); public final ArrayList permissionGroups = new ArrayList(0); public final ArrayList activities = new ArrayList(0); public final ArrayList receivers = new ArrayList(0); public final ArrayList providers = new ArrayList(0); public final ArrayList services = new ArrayList(0); public final ArrayList instrumentation = new ArrayList(0); public final ArrayList requestedPermissions = new ArrayList(); public ArrayList protectedBroadcasts; public Package parentPackage; public ArrayList childPackages; public String staticSharedLibName = null; public int staticSharedLibVersion = 0; public ArrayList libraryNames = null; public ArrayList usesLibraries = null; public ArrayList usesStaticLibraries = null; public int[] usesStaticLibrariesVersions = null; public String[] usesStaticLibrariesCertDigests = null; public ArrayList usesOptionalLibraries = null; public String[] usesLibraryFiles = null; public ArrayList preferredActivityFilters = null; public ArrayList mOriginalPackages = null; public String mRealPackage = null; public ArrayList mAdoptPermissions = null; // We store the application meta-data independently to avoid multiple unwanted references public Bundle mAppMetaData = null; // The version code declared for this package. public int mVersionCode; // The version name declared for this package. public String mVersionName; // The shared user id that this package wants to use. public String mSharedUserId; // The shared user label that this package wants to use. public int mSharedUserLabel; // Signatures that were read from the package. public Signature[] mSignatures; public Certificate[][] mCertificates; // For use by package manager service for quick lookup of // preferred up order. public int mPreferredOrder = 0; // For use by package manager to keep track of when a package was last used. public long[] mLastPackageUsageTimeInMills = new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT]; // // User set enabled state. // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; // // // Whether the package has been stopped. // public boolean mSetStopped = false; // Additional data supplied by callers. public Object mExtras; // Applications hardware preferences public ArrayList configPreferences = null; // Applications requested features public ArrayList reqFeatures = null; // Applications requested feature groups public ArrayList featureGroups = null; public int installLocation; public boolean coreApp; /* An app that's required for all users and cannot be uninstalled for a user */ public boolean mRequiredForAllUsers; /* The restricted account authenticator type that is used by this application */ public String mRestrictedAccountType; /* The required account type without which this application will not function */ public String mRequiredAccountType; public String mOverlayTarget; public int mOverlayPriority; public boolean mIsStaticOverlay; public boolean mTrustedOverlay; /** * Data used to feed the KeySetManagerService */ public ArraySet mSigningKeys; public ArraySet mUpgradeKeySets; public ArrayMap> mKeySetMapping; /** * The install time abi override for this package, if any. * * TODO: This seems like a horrible place to put the abiOverride because * this isn't something the packageParser parsers. However, this fits in with * the rest of the PackageManager where package scanning randomly pushes * and prods fields out of {@code this.applicationInfo}. */ public String cpuAbiOverride; /** * The install time abi override to choose 32bit abi's when multiple abi's * are present. This is only meaningfull for multiarch applications. * The use32bitAbi attribute is ignored if cpuAbiOverride is also set. */ public boolean use32bitAbi; public byte[] restrictUpdateHash; /** * Set if the app or any of its components are visible to Instant Apps. */ public boolean visibleToInstantApps; public Package(String packageName) { this.packageName = packageName; this.manifestPackageName = packageName; applicationInfo.packageName = packageName; applicationInfo.uid = -1; } public void setApplicationVolumeUuid(String volumeUuid) { final UUID storageUuid = StorageManager.convert(volumeUuid); this.applicationInfo.volumeUuid = volumeUuid; this.applicationInfo.storageUuid = storageUuid; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.volumeUuid = volumeUuid; childPackages.get(i).applicationInfo.storageUuid = storageUuid; } } } public void setApplicationInfoCodePath(String codePath) { this.applicationInfo.setCodePath(codePath); if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.setCodePath(codePath); } } } public void setApplicationInfoResourcePath(String resourcePath) { this.applicationInfo.setResourcePath(resourcePath); if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.setResourcePath(resourcePath); } } } public void setApplicationInfoBaseResourcePath(String resourcePath) { this.applicationInfo.setBaseResourcePath(resourcePath); if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.setBaseResourcePath(resourcePath); } } } public void setApplicationInfoBaseCodePath(String baseCodePath) { this.applicationInfo.setBaseCodePath(baseCodePath); if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.setBaseCodePath(baseCodePath); } } } public List getChildPackageNames() { if (childPackages == null) { return null; } final int childCount = childPackages.size(); final List childPackageNames = new ArrayList<>(childCount); for (int i = 0; i < childCount; i++) { String childPackageName = childPackages.get(i).packageName; childPackageNames.add(childPackageName); } return childPackageNames; } public boolean hasChildPackage(String packageName) { final int childCount = (childPackages != null) ? childPackages.size() : 0; for (int i = 0; i < childCount; i++) { if (childPackages.get(i).packageName.equals(packageName)) { return true; } } return false; } public void setApplicationInfoSplitCodePaths(String[] splitCodePaths) { this.applicationInfo.setSplitCodePaths(splitCodePaths); // Children have no splits } public void setApplicationInfoSplitResourcePaths(String[] resroucePaths) { this.applicationInfo.setSplitResourcePaths(resroucePaths); // Children have no splits } public void setSplitCodePaths(String[] codePaths) { this.splitCodePaths = codePaths; } public void setCodePath(String codePath) { this.codePath = codePath; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).codePath = codePath; } } } public void setBaseCodePath(String baseCodePath) { this.baseCodePath = baseCodePath; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).baseCodePath = baseCodePath; } } } public void setSignatures(Signature[] signatures) { this.mSignatures = signatures; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).mSignatures = signatures; } } } public void setVolumeUuid(String volumeUuid) { this.volumeUuid = volumeUuid; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).volumeUuid = volumeUuid; } } } public void setApplicationInfoFlags(int mask, int flags) { applicationInfo.flags = (applicationInfo.flags & ~mask) | (mask & flags); if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).applicationInfo.flags = (applicationInfo.flags & ~mask) | (mask & flags); } } } public void setUse32bitAbi(boolean use32bitAbi) { this.use32bitAbi = use32bitAbi; if (childPackages != null) { final int packageCount = childPackages.size(); for (int i = 0; i < packageCount; i++) { childPackages.get(i).use32bitAbi = use32bitAbi; } } } public boolean isLibrary() { return staticSharedLibName != null || !ArrayUtils.isEmpty(libraryNames); } public List getAllCodePaths() { ArrayList paths = new ArrayList<>(); paths.add(baseCodePath); if (!ArrayUtils.isEmpty(splitCodePaths)) { Collections.addAll(paths, splitCodePaths); } return paths; } /** * Filtered set of {@link #getAllCodePaths()} that excludes * resource-only APKs. */ public List getAllCodePathsExcludingResourceOnly() { ArrayList paths = new ArrayList<>(); if ((applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { paths.add(baseCodePath); } if (!ArrayUtils.isEmpty(splitCodePaths)) { for (int i = 0; i < splitCodePaths.length; i++) { if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { paths.add(splitCodePaths[i]); } } } return paths; } public void setPackageName(String newName) { packageName = newName; applicationInfo.packageName = newName; for (int i=permissions.size()-1; i>=0; i--) { permissions.get(i).setPackageName(newName); } for (int i=permissionGroups.size()-1; i>=0; i--) { permissionGroups.get(i).setPackageName(newName); } for (int i=activities.size()-1; i>=0; i--) { activities.get(i).setPackageName(newName); } for (int i=receivers.size()-1; i>=0; i--) { receivers.get(i).setPackageName(newName); } for (int i=providers.size()-1; i>=0; i--) { providers.get(i).setPackageName(newName); } for (int i=services.size()-1; i>=0; i--) { services.get(i).setPackageName(newName); } for (int i=instrumentation.size()-1; i>=0; i--) { instrumentation.get(i).setPackageName(newName); } } public boolean hasComponentClassName(String name) { for (int i=activities.size()-1; i>=0; i--) { if (name.equals(activities.get(i).className)) { return true; } } for (int i=receivers.size()-1; i>=0; i--) { if (name.equals(receivers.get(i).className)) { return true; } } for (int i=providers.size()-1; i>=0; i--) { if (name.equals(providers.get(i).className)) { return true; } } for (int i=services.size()-1; i>=0; i--) { if (name.equals(services.get(i).className)) { return true; } } for (int i=instrumentation.size()-1; i>=0; i--) { if (name.equals(instrumentation.get(i).className)) { return true; } } return false; } /** * @hide */ public boolean isForwardLocked() { return applicationInfo.isForwardLocked(); } /** * @hide */ public boolean isSystemApp() { return applicationInfo.isSystemApp(); } /** * @hide */ public boolean isPrivilegedApp() { return applicationInfo.isPrivilegedApp(); } /** * @hide */ public boolean isUpdatedSystemApp() { return applicationInfo.isUpdatedSystemApp(); } /** * @hide */ public boolean canHaveOatDir() { // The following app types CANNOT have oat directory // - non-updated system apps // - forward-locked apps or apps installed in ASEC containers return (!isSystemApp() || isUpdatedSystemApp()) && !isForwardLocked() && !applicationInfo.isExternalAsec(); } public boolean isMatch(int flags) { if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { return isSystemApp(); } return true; } public long getLatestPackageUseTimeInMills() { long latestUse = 0L; for (long use : mLastPackageUsageTimeInMills) { latestUse = Math.max(latestUse, use); } return latestUse; } public long getLatestForegroundPackageUseTimeInMills() { int[] foregroundReasons = { PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY, PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE }; long latestUse = 0L; for (int reason : foregroundReasons) { latestUse = Math.max(latestUse, mLastPackageUsageTimeInMills[reason]); } return latestUse; } public String toString() { return "Package{" + Integer.toHexString(System.identityHashCode(this)) + " " + packageName + "}"; } @Override public int describeContents() { return 0; } public Package(Parcel dest) { // We use the boot classloader for all classes that we load. final ClassLoader boot = Object.class.getClassLoader(); packageName = dest.readString().intern(); manifestPackageName = dest.readString(); splitNames = dest.readStringArray(); volumeUuid = dest.readString(); codePath = dest.readString(); baseCodePath = dest.readString(); splitCodePaths = dest.readStringArray(); baseRevisionCode = dest.readInt(); splitRevisionCodes = dest.createIntArray(); splitFlags = dest.createIntArray(); splitPrivateFlags = dest.createIntArray(); baseHardwareAccelerated = (dest.readInt() == 1); applicationInfo = dest.readParcelable(boot); if (applicationInfo.permission != null) { applicationInfo.permission = applicationInfo.permission.intern(); } // We don't serialize the "owner" package and the application info object for each of // these components, in order to save space and to avoid circular dependencies while // serialization. We need to fix them all up here. dest.readParcelableList(permissions, boot); fixupOwner(permissions); dest.readParcelableList(permissionGroups, boot); fixupOwner(permissionGroups); dest.readParcelableList(activities, boot); fixupOwner(activities); dest.readParcelableList(receivers, boot); fixupOwner(receivers); dest.readParcelableList(providers, boot); fixupOwner(providers); dest.readParcelableList(services, boot); fixupOwner(services); dest.readParcelableList(instrumentation, boot); fixupOwner(instrumentation); dest.readStringList(requestedPermissions); internStringArrayList(requestedPermissions); protectedBroadcasts = dest.createStringArrayList(); internStringArrayList(protectedBroadcasts); parentPackage = dest.readParcelable(boot); childPackages = new ArrayList<>(); dest.readParcelableList(childPackages, boot); if (childPackages.size() == 0) { childPackages = null; } staticSharedLibName = dest.readString(); if (staticSharedLibName != null) { staticSharedLibName = staticSharedLibName.intern(); } staticSharedLibVersion = dest.readInt(); libraryNames = dest.createStringArrayList(); internStringArrayList(libraryNames); usesLibraries = dest.createStringArrayList(); internStringArrayList(usesLibraries); usesOptionalLibraries = dest.createStringArrayList(); internStringArrayList(usesOptionalLibraries); usesLibraryFiles = dest.readStringArray(); final int libCount = dest.readInt(); if (libCount > 0) { usesStaticLibraries = new ArrayList<>(libCount); dest.readStringList(usesStaticLibraries); internStringArrayList(usesStaticLibraries); usesStaticLibrariesVersions = new int[libCount]; dest.readIntArray(usesStaticLibrariesVersions); usesStaticLibrariesCertDigests = new String[libCount]; dest.readStringArray(usesStaticLibrariesCertDigests); } preferredActivityFilters = new ArrayList<>(); dest.readParcelableList(preferredActivityFilters, boot); if (preferredActivityFilters.size() == 0) { preferredActivityFilters = null; } mOriginalPackages = dest.createStringArrayList(); mRealPackage = dest.readString(); mAdoptPermissions = dest.createStringArrayList(); mAppMetaData = dest.readBundle(); mVersionCode = dest.readInt(); mVersionName = dest.readString(); if (mVersionName != null) { mVersionName = mVersionName.intern(); } mSharedUserId = dest.readString(); if (mSharedUserId != null) { mSharedUserId = mSharedUserId.intern(); } mSharedUserLabel = dest.readInt(); mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class); mCertificates = (Certificate[][]) dest.readSerializable(); mPreferredOrder = dest.readInt(); // long[] packageUsageTimeMillis is not persisted because it isn't information that // is parsed from the APK. // Object mExtras is not persisted because it is not information that is read from // the APK, rather, it is supplied by callers. configPreferences = new ArrayList<>(); dest.readParcelableList(configPreferences, boot); if (configPreferences.size() == 0) { configPreferences = null; } reqFeatures = new ArrayList<>(); dest.readParcelableList(reqFeatures, boot); if (reqFeatures.size() == 0) { reqFeatures = null; } featureGroups = new ArrayList<>(); dest.readParcelableList(featureGroups, boot); if (featureGroups.size() == 0) { featureGroups = null; } installLocation = dest.readInt(); coreApp = (dest.readInt() == 1); mRequiredForAllUsers = (dest.readInt() == 1); mRestrictedAccountType = dest.readString(); mRequiredAccountType = dest.readString(); mOverlayTarget = dest.readString(); mOverlayPriority = dest.readInt(); mIsStaticOverlay = (dest.readInt() == 1); mTrustedOverlay = (dest.readInt() == 1); mSigningKeys = (ArraySet) dest.readArraySet(boot); mUpgradeKeySets = (ArraySet) dest.readArraySet(boot); mKeySetMapping = readKeySetMapping(dest); cpuAbiOverride = dest.readString(); use32bitAbi = (dest.readInt() == 1); restrictUpdateHash = dest.createByteArray(); visibleToInstantApps = dest.readInt() == 1; } private static void internStringArrayList(List list) { if (list != null) { final int N = list.size(); for (int i = 0; i < N; ++i) { list.set(i, list.get(i).intern()); } } } /** * Sets the package owner and the the {@code applicationInfo} for every component * owner by this package. */ private void fixupOwner(List> list) { if (list != null) { for (Component c : list) { c.owner = this; if (c instanceof Activity) { ((Activity) c).info.applicationInfo = this.applicationInfo; } else if (c instanceof Service) { ((Service) c).info.applicationInfo = this.applicationInfo; } else if (c instanceof Provider) { ((Provider) c).info.applicationInfo = this.applicationInfo; } } } } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(packageName); dest.writeString(manifestPackageName); dest.writeStringArray(splitNames); dest.writeString(volumeUuid); dest.writeString(codePath); dest.writeString(baseCodePath); dest.writeStringArray(splitCodePaths); dest.writeInt(baseRevisionCode); dest.writeIntArray(splitRevisionCodes); dest.writeIntArray(splitFlags); dest.writeIntArray(splitPrivateFlags); dest.writeInt(baseHardwareAccelerated ? 1 : 0); dest.writeParcelable(applicationInfo, flags); dest.writeParcelableList(permissions, flags); dest.writeParcelableList(permissionGroups, flags); dest.writeParcelableList(activities, flags); dest.writeParcelableList(receivers, flags); dest.writeParcelableList(providers, flags); dest.writeParcelableList(services, flags); dest.writeParcelableList(instrumentation, flags); dest.writeStringList(requestedPermissions); dest.writeStringList(protectedBroadcasts); dest.writeParcelable(parentPackage, flags); dest.writeParcelableList(childPackages, flags); dest.writeString(staticSharedLibName); dest.writeInt(staticSharedLibVersion); dest.writeStringList(libraryNames); dest.writeStringList(usesLibraries); dest.writeStringList(usesOptionalLibraries); dest.writeStringArray(usesLibraryFiles); if (ArrayUtils.isEmpty(usesStaticLibraries)) { dest.writeInt(-1); } else { dest.writeInt(usesStaticLibraries.size()); dest.writeStringList(usesStaticLibraries); dest.writeIntArray(usesStaticLibrariesVersions); dest.writeStringArray(usesStaticLibrariesCertDigests); } dest.writeParcelableList(preferredActivityFilters, flags); dest.writeStringList(mOriginalPackages); dest.writeString(mRealPackage); dest.writeStringList(mAdoptPermissions); dest.writeBundle(mAppMetaData); dest.writeInt(mVersionCode); dest.writeString(mVersionName); dest.writeString(mSharedUserId); dest.writeInt(mSharedUserLabel); dest.writeParcelableArray(mSignatures, flags); dest.writeSerializable(mCertificates); dest.writeInt(mPreferredOrder); // long[] packageUsageTimeMillis is not persisted because it isn't information that // is parsed from the APK. // Object mExtras is not persisted because it is not information that is read from // the APK, rather, it is supplied by callers. dest.writeParcelableList(configPreferences, flags); dest.writeParcelableList(reqFeatures, flags); dest.writeParcelableList(featureGroups, flags); dest.writeInt(installLocation); dest.writeInt(coreApp ? 1 : 0); dest.writeInt(mRequiredForAllUsers ? 1 : 0); dest.writeString(mRestrictedAccountType); dest.writeString(mRequiredAccountType); dest.writeString(mOverlayTarget); dest.writeInt(mOverlayPriority); dest.writeInt(mIsStaticOverlay ? 1 : 0); dest.writeInt(mTrustedOverlay ? 1 : 0); dest.writeArraySet(mSigningKeys); dest.writeArraySet(mUpgradeKeySets); writeKeySetMapping(dest, mKeySetMapping); dest.writeString(cpuAbiOverride); dest.writeInt(use32bitAbi ? 1 : 0); dest.writeByteArray(restrictUpdateHash); dest.writeInt(visibleToInstantApps ? 1 : 0); } /** * Writes the keyset mapping to the provided package. {@code null} mappings are permitted. */ private static void writeKeySetMapping( Parcel dest, ArrayMap> keySetMapping) { if (keySetMapping == null) { dest.writeInt(-1); return; } final int N = keySetMapping.size(); dest.writeInt(N); for (int i = 0; i < N; i++) { dest.writeString(keySetMapping.keyAt(i)); ArraySet keys = keySetMapping.valueAt(i); if (keys == null) { dest.writeInt(-1); continue; } final int M = keys.size(); dest.writeInt(M); for (int j = 0; j < M; j++) { dest.writeSerializable(keys.valueAt(j)); } } } /** * Reads a keyset mapping from the given parcel at the given data position. May return * {@code null} if the serialized mapping was {@code null}. */ private static ArrayMap> readKeySetMapping(Parcel in) { final int N = in.readInt(); if (N == -1) { return null; } ArrayMap> keySetMapping = new ArrayMap<>(); for (int i = 0; i < N; ++i) { String key = in.readString(); final int M = in.readInt(); if (M == -1) { keySetMapping.put(key, null); continue; } ArraySet keys = new ArraySet<>(M); for (int j = 0; j < M; ++j) { PublicKey pk = (PublicKey) in.readSerializable(); keys.add(pk); } keySetMapping.put(key, keys); } return keySetMapping; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Package createFromParcel(Parcel in) { return new Package(in); } public Package[] newArray(int size) { return new Package[size]; } }; } public static abstract class Component { public final ArrayList intents; public final String className; public Bundle metaData; public Package owner; ComponentName componentName; String componentShortName; public Component(Package _owner) { owner = _owner; intents = null; className = null; } public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) { owner = args.owner; intents = new ArrayList(0); if (parsePackageItemInfo(args.owner, outInfo, args.outError, args.tag, args.sa, true /*nameRequired*/, args.nameRes, args.labelRes, args.iconRes, args.roundIconRes, args.logoRes, args.bannerRes)) { className = outInfo.name; } else { className = null; } } public Component(final ParseComponentArgs args, final ComponentInfo outInfo) { this(args, (PackageItemInfo)outInfo); if (args.outError[0] != null) { return; } if (args.processRes != 0) { CharSequence pname; if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) { pname = args.sa.getNonConfigurationString(args.processRes, Configuration.NATIVE_CONFIG_VERSION); } else { // Some older apps have been seen to use a resource reference // here that on older builds was ignored (with a warning). We // need to continue to do this for them so they don't break. pname = args.sa.getNonResourceString(args.processRes); } outInfo.processName = buildProcessName(owner.applicationInfo.packageName, owner.applicationInfo.processName, pname, args.flags, args.sepProcesses, args.outError); } if (args.descriptionRes != 0) { outInfo.descriptionRes = args.sa.getResourceId(args.descriptionRes, 0); } outInfo.enabled = args.sa.getBoolean(args.enabledRes, true); } public Component(Component clone) { owner = clone.owner; intents = clone.intents; className = clone.className; componentName = clone.componentName; componentShortName = clone.componentShortName; } public ComponentName getComponentName() { if (componentName != null) { return componentName; } if (className != null) { componentName = new ComponentName(owner.applicationInfo.packageName, className); } return componentName; } protected Component(Parcel in) { className = in.readString(); metaData = in.readBundle(); intents = createIntentsList(in); owner = null; } protected void writeToParcel(Parcel dest, int flags) { dest.writeString(className); dest.writeBundle(metaData); writeIntentsList(intents, dest, flags); } /** *

* Implementation note: The serialized form for the intent list also contains the name * of the concrete class that's stored in the list, and assumes that every element of the * list is of the same type. This is very similar to the original parcelable mechanism. * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable * and is public API. It also declares Parcelable related methods as final which means * we can't extend them. The approach of using composition instead of inheritance leads to * a large set of cascading changes in the PackageManagerService, which seem undesirable. * *

* WARNING: The list of objects returned by this function might need to be fixed up * to make sure their owner fields are consistent. See {@code fixupOwner}. */ private static void writeIntentsList(ArrayList list, Parcel out, int flags) { if (list == null) { out.writeInt(-1); return; } final int N = list.size(); out.writeInt(N); // Don't bother writing the component name if the list is empty. if (N > 0) { IntentInfo info = list.get(0); out.writeString(info.getClass().getName()); for (int i = 0; i < N;i++) { list.get(i).writeIntentInfoToParcel(out, flags); } } } private static ArrayList createIntentsList(Parcel in) { int N = in.readInt(); if (N == -1) { return null; } if (N == 0) { return new ArrayList<>(0); } String componentName = in.readString(); final ArrayList intentsList; try { final Class cls = (Class) Class.forName(componentName); final Constructor cons = cls.getConstructor(Parcel.class); intentsList = new ArrayList<>(N); for (int i = 0; i < N; ++i) { intentsList.add(cons.newInstance(in)); } } catch (ReflectiveOperationException ree) { throw new AssertionError("Unable to construct intent list for: " + componentName); } return intentsList; } public void appendComponentShortName(StringBuilder sb) { ComponentName.appendShortString(sb, owner.applicationInfo.packageName, className); } public void printComponentShortName(PrintWriter pw) { ComponentName.printShortString(pw, owner.applicationInfo.packageName, className); } public void setPackageName(String packageName) { componentName = null; componentShortName = null; } } public final static class Permission extends Component implements Parcelable { public final PermissionInfo info; public boolean tree; public PermissionGroup group; public Permission(Package _owner) { super(_owner); info = new PermissionInfo(); } public Permission(Package _owner, PermissionInfo _info) { super(_owner); info = _info; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } public String toString() { return "Permission{" + Integer.toHexString(System.identityHashCode(this)) + " " + info.name + "}"; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags); dest.writeInt(tree ? 1 : 0); dest.writeParcelable(group, flags); } private Permission(Parcel in) { super(in); final ClassLoader boot = Object.class.getClassLoader(); info = in.readParcelable(boot); if (info.group != null) { info.group = info.group.intern(); } tree = (in.readInt() == 1); group = in.readParcelable(boot); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Permission createFromParcel(Parcel in) { return new Permission(in); } public Permission[] newArray(int size) { return new Permission[size]; } }; } public final static class PermissionGroup extends Component implements Parcelable { public final PermissionGroupInfo info; public PermissionGroup(Package _owner) { super(_owner); info = new PermissionGroupInfo(); } public PermissionGroup(Package _owner, PermissionGroupInfo _info) { super(_owner); info = _info; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } public String toString() { return "PermissionGroup{" + Integer.toHexString(System.identityHashCode(this)) + " " + info.name + "}"; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags); } private PermissionGroup(Parcel in) { super(in); info = in.readParcelable(Object.class.getClassLoader()); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public PermissionGroup createFromParcel(Parcel in) { return new PermissionGroup(in); } public PermissionGroup[] newArray(int size) { return new PermissionGroup[size]; } }; } private static boolean copyNeeded(int flags, Package p, PackageUserState state, Bundle metaData, int userId) { if (userId != UserHandle.USER_SYSTEM) { // We always need to copy for other users, since we need // to fix up the uid. return true; } if (state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { boolean enabled = state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; if (p.applicationInfo.enabled != enabled) { return true; } } boolean suspended = (p.applicationInfo.flags & FLAG_SUSPENDED) != 0; if (state.suspended != suspended) { return true; } if (!state.installed || state.hidden) { return true; } if (state.stopped) { return true; } if (state.instantApp != p.applicationInfo.isInstantApp()) { return true; } if ((flags & PackageManager.GET_META_DATA) != 0 && (metaData != null || p.mAppMetaData != null)) { return true; } if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0 && p.usesLibraryFiles != null) { return true; } if (p.staticSharedLibName != null) { return true; } return false; } public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state) { return generateApplicationInfo(p, flags, state, UserHandle.getCallingUserId()); } private static void updateApplicationInfo(ApplicationInfo ai, int flags, PackageUserState state) { // CompatibilityMode is global state. if (!sCompatibilityModeEnabled) { ai.disableCompatibilityMode(); } if (state.installed) { ai.flags |= ApplicationInfo.FLAG_INSTALLED; } else { ai.flags &= ~ApplicationInfo.FLAG_INSTALLED; } if (state.suspended) { ai.flags |= ApplicationInfo.FLAG_SUSPENDED; } else { ai.flags &= ~ApplicationInfo.FLAG_SUSPENDED; } if (state.instantApp) { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_INSTANT; } else { ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT; } if (state.hidden) { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN; } else { ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN; } if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { ai.enabled = true; } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { ai.enabled = (flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0; } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { ai.enabled = false; } ai.enabledSetting = state.enabled; if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { ai.category = state.categoryHint; } if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); } ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); ai.resourceDirs = state.overlayPaths; } public static ApplicationInfo generateApplicationInfo(Package p, int flags, PackageUserState state, int userId) { if (p == null) return null; if (!checkUseInstalledOrHidden(flags, state, p.applicationInfo) || !p.isMatch(flags)) { return null; } if (!copyNeeded(flags, p, state, null, userId) && ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0 || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) { // In this case it is safe to directly modify the internal ApplicationInfo state: // - CompatibilityMode is global state, so will be the same for every call. // - We only come in to here if the app should reported as installed; this is the // default state, and we will do a copy otherwise. // - The enable state will always be reported the same for the application across // calls; the only exception is for the UNTIL_USED mode, and in that case we will // be doing a copy. updateApplicationInfo(p.applicationInfo, flags, state); return p.applicationInfo; } // Make shallow copy so we can store the metadata/libraries safely ApplicationInfo ai = new ApplicationInfo(p.applicationInfo); ai.initForUser(userId); if ((flags & PackageManager.GET_META_DATA) != 0) { ai.metaData = p.mAppMetaData; } if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) { ai.sharedLibraryFiles = p.usesLibraryFiles; } if (state.stopped) { ai.flags |= ApplicationInfo.FLAG_STOPPED; } else { ai.flags &= ~ApplicationInfo.FLAG_STOPPED; } updateApplicationInfo(ai, flags, state); return ai; } public static ApplicationInfo generateApplicationInfo(ApplicationInfo ai, int flags, PackageUserState state, int userId) { if (ai == null) return null; if (!checkUseInstalledOrHidden(flags, state, ai)) { return null; } // This is only used to return the ResolverActivity; we will just always // make a copy. ai = new ApplicationInfo(ai); ai.initForUser(userId); if (state.stopped) { ai.flags |= ApplicationInfo.FLAG_STOPPED; } else { ai.flags &= ~ApplicationInfo.FLAG_STOPPED; } updateApplicationInfo(ai, flags, state); return ai; } public static final PermissionInfo generatePermissionInfo( Permission p, int flags) { if (p == null) return null; if ((flags&PackageManager.GET_META_DATA) == 0) { return p.info; } PermissionInfo pi = new PermissionInfo(p.info); pi.metaData = p.metaData; return pi; } public static final PermissionGroupInfo generatePermissionGroupInfo( PermissionGroup pg, int flags) { if (pg == null) return null; if ((flags&PackageManager.GET_META_DATA) == 0) { return pg.info; } PermissionGroupInfo pgi = new PermissionGroupInfo(pg.info); pgi.metaData = pg.metaData; return pgi; } public final static class Activity extends Component implements Parcelable { public final ActivityInfo info; public Activity(final ParseComponentArgs args, final ActivityInfo _info) { super(args, _info); info = _info; info.applicationInfo = args.owner.applicationInfo; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Activity{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); appendComponentShortName(sb); sb.append('}'); return sb.toString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); } private Activity(Parcel in) { super(in); info = in.readParcelable(Object.class.getClassLoader()); for (ActivityIntentInfo aii : intents) { aii.activity = this; } if (info.permission != null) { info.permission = info.permission.intern(); } } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Activity createFromParcel(Parcel in) { return new Activity(in); } public Activity[] newArray(int size) { return new Activity[size]; } }; } public static final ActivityInfo generateActivityInfo(Activity a, int flags, PackageUserState state, int userId) { if (a == null) return null; if (!checkUseInstalledOrHidden(flags, state, a.owner.applicationInfo)) { return null; } if (!copyNeeded(flags, a.owner, state, a.metaData, userId)) { updateApplicationInfo(a.info.applicationInfo, flags, state); return a.info; } // Make shallow copies so we can store the metadata safely ActivityInfo ai = new ActivityInfo(a.info); ai.metaData = a.metaData; ai.applicationInfo = generateApplicationInfo(a.owner, flags, state, userId); return ai; } public static final ActivityInfo generateActivityInfo(ActivityInfo ai, int flags, PackageUserState state, int userId) { if (ai == null) return null; if (!checkUseInstalledOrHidden(flags, state, ai.applicationInfo)) { return null; } // This is only used to return the ResolverActivity; we will just always // make a copy. ai = new ActivityInfo(ai); ai.applicationInfo = generateApplicationInfo(ai.applicationInfo, flags, state, userId); return ai; } public final static class Service extends Component implements Parcelable { public final ServiceInfo info; public Service(final ParseComponentArgs args, final ServiceInfo _info) { super(args, _info); info = _info; info.applicationInfo = args.owner.applicationInfo; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Service{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); appendComponentShortName(sb); sb.append('}'); return sb.toString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); } private Service(Parcel in) { super(in); info = in.readParcelable(Object.class.getClassLoader()); for (ServiceIntentInfo aii : intents) { aii.service = this; } if (info.permission != null) { info.permission = info.permission.intern(); } } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Service createFromParcel(Parcel in) { return new Service(in); } public Service[] newArray(int size) { return new Service[size]; } }; } public static final ServiceInfo generateServiceInfo(Service s, int flags, PackageUserState state, int userId) { if (s == null) return null; if (!checkUseInstalledOrHidden(flags, state, s.owner.applicationInfo)) { return null; } if (!copyNeeded(flags, s.owner, state, s.metaData, userId)) { updateApplicationInfo(s.info.applicationInfo, flags, state); return s.info; } // Make shallow copies so we can store the metadata safely ServiceInfo si = new ServiceInfo(s.info); si.metaData = s.metaData; si.applicationInfo = generateApplicationInfo(s.owner, flags, state, userId); return si; } public final static class Provider extends Component implements Parcelable { public final ProviderInfo info; public boolean syncable; public Provider(final ParseComponentArgs args, final ProviderInfo _info) { super(args, _info); info = _info; info.applicationInfo = args.owner.applicationInfo; syncable = false; } public Provider(Provider existingProvider) { super(existingProvider); this.info = existingProvider.info; this.syncable = existingProvider.syncable; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Provider{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); appendComponentShortName(sb); sb.append('}'); return sb.toString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); dest.writeInt((syncable) ? 1 : 0); } private Provider(Parcel in) { super(in); info = in.readParcelable(Object.class.getClassLoader()); syncable = (in.readInt() == 1); for (ProviderIntentInfo aii : intents) { aii.provider = this; } if (info.readPermission != null) { info.readPermission = info.readPermission.intern(); } if (info.writePermission != null) { info.writePermission = info.writePermission.intern(); } if (info.authority != null) { info.authority = info.authority.intern(); } } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Provider createFromParcel(Parcel in) { return new Provider(in); } public Provider[] newArray(int size) { return new Provider[size]; } }; } public static final ProviderInfo generateProviderInfo(Provider p, int flags, PackageUserState state, int userId) { if (p == null) return null; if (!checkUseInstalledOrHidden(flags, state, p.owner.applicationInfo)) { return null; } if (!copyNeeded(flags, p.owner, state, p.metaData, userId) && ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) != 0 || p.info.uriPermissionPatterns == null)) { updateApplicationInfo(p.info.applicationInfo, flags, state); return p.info; } // Make shallow copies so we can store the metadata safely ProviderInfo pi = new ProviderInfo(p.info); pi.metaData = p.metaData; if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) { pi.uriPermissionPatterns = null; } pi.applicationInfo = generateApplicationInfo(p.owner, flags, state, userId); return pi; } public final static class Instrumentation extends Component implements Parcelable { public final InstrumentationInfo info; public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) { super(args, _info); info = _info; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Instrumentation{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); appendComponentShortName(sb); sb.append('}'); return sb.toString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags); } private Instrumentation(Parcel in) { super(in); info = in.readParcelable(Object.class.getClassLoader()); if (info.targetPackage != null) { info.targetPackage = info.targetPackage.intern(); } if (info.targetProcesses != null) { info.targetProcesses = info.targetProcesses.intern(); } } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public Instrumentation createFromParcel(Parcel in) { return new Instrumentation(in); } public Instrumentation[] newArray(int size) { return new Instrumentation[size]; } }; } public static final InstrumentationInfo generateInstrumentationInfo( Instrumentation i, int flags) { if (i == null) return null; if ((flags&PackageManager.GET_META_DATA) == 0) { return i.info; } InstrumentationInfo ii = new InstrumentationInfo(i.info); ii.metaData = i.metaData; return ii; } public static abstract class IntentInfo extends IntentFilter { public boolean hasDefault; public int labelRes; public CharSequence nonLocalizedLabel; public int icon; public int logo; public int banner; public int preferred; protected IntentInfo() { } protected IntentInfo(Parcel dest) { super(dest); hasDefault = (dest.readInt() == 1); labelRes = dest.readInt(); nonLocalizedLabel = dest.readCharSequence(); icon = dest.readInt(); logo = dest.readInt(); banner = dest.readInt(); preferred = dest.readInt(); } public void writeIntentInfoToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(hasDefault ? 1 : 0); dest.writeInt(labelRes); dest.writeCharSequence(nonLocalizedLabel); dest.writeInt(icon); dest.writeInt(logo); dest.writeInt(banner); dest.writeInt(preferred); } } public final static class ActivityIntentInfo extends IntentInfo { public Activity activity; public ActivityIntentInfo(Activity _activity) { activity = _activity; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("ActivityIntentInfo{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); activity.appendComponentShortName(sb); sb.append('}'); return sb.toString(); } public ActivityIntentInfo(Parcel in) { super(in); } } public final static class ServiceIntentInfo extends IntentInfo { public Service service; public ServiceIntentInfo(Service _service) { service = _service; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("ServiceIntentInfo{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); service.appendComponentShortName(sb); sb.append('}'); return sb.toString(); } public ServiceIntentInfo(Parcel in) { super(in); } } public static final class ProviderIntentInfo extends IntentInfo { public Provider provider; public ProviderIntentInfo(Provider provider) { this.provider = provider; } public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("ProviderIntentInfo{"); sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(' '); provider.appendComponentShortName(sb); sb.append('}'); return sb.toString(); } public ProviderIntentInfo(Parcel in) { super(in); } } /** * @hide */ public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) { sCompatibilityModeEnabled = compatibilityModeEnabled; } private static AtomicReference sBuffer = new AtomicReference(); public static long readFullyIgnoringContents(InputStream in) throws IOException { byte[] buffer = sBuffer.getAndSet(null); if (buffer == null) { buffer = new byte[4096]; } int n = 0; int count = 0; while ((n = in.read(buffer, 0, buffer.length)) != -1) { count += n; } sBuffer.set(buffer); return count; } public static void closeQuietly(StrictJarFile jarFile) { if (jarFile != null) { try { jarFile.close(); } catch (Exception ignored) { } } } public static class PackageParserException extends Exception { public final int error; public PackageParserException(int error, String detailMessage) { super(detailMessage); this.error = error; } public PackageParserException(int error, String detailMessage, Throwable throwable) { super(detailMessage, throwable); this.error = error; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy