Import Android SDK Platform P [4697573]
/google/data/ro/projects/android/fetch_artifact \
--bid 4697573 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4697573.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: If80578c3c657366cc9cf75f8db13d46e2dd4e077
diff --git a/android/content/pm/AndroidTestBaseUpdater.java b/android/content/pm/AndroidTestBaseUpdater.java
new file mode 100644
index 0000000..2aaac02
--- /dev/null
+++ b/android/content/pm/AndroidTestBaseUpdater.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that if it targets < P that the android.test.base library is
+ * included by default.
+ *
+ * <p>This is separated out so that it can be conditionally included at build time depending on
+ * whether android.test.base is on the bootclasspath or not. In order to include this at
+ * build time, and remove android.test.base from the bootclasspath pass
+ * REMOVE_ATB_FROM_BCP=true on the build command line, otherwise this class will not be included
+ * and the
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater {
+
+ @Override
+ public void updatePackage(Package pkg) {
+ // Packages targeted at <= O_MR1 expect the classes in the android.test.base library
+ // to be accessible so this maintains backward compatibility by adding the
+ // android.test.base library to those packages.
+ if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
+ prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
+ } else {
+ // If a package already depends on android.test.runner then add a dependency on
+ // android.test.base because android.test.runner depends on classes from the
+ // android.test.base library.
+ prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_BASE);
+ }
+ }
+}
diff --git a/android/content/pm/ApplicationInfo.java b/android/content/pm/ApplicationInfo.java
index 746a090..e85058d 100644
--- a/android/content/pm/ApplicationInfo.java
+++ b/android/content/pm/ApplicationInfo.java
@@ -37,6 +37,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -602,6 +603,13 @@
*/
public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
+ /**
+ * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+ * product partition of the system image.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -619,6 +627,7 @@
PRIVATE_FLAG_OEM,
PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
PRIVATE_FLAG_PRIVILEGED,
+ PRIVATE_FLAG_PRODUCT,
PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
PRIVATE_FLAG_VENDOR,
@@ -754,15 +763,13 @@
public String[] resourceDirs;
/**
- * String retrieved from the seinfo tag found in selinux policy. This value
- * can be overridden with a value set through the mac_permissions.xml policy
- * construct. This value is useful in setting an SELinux security context on
- * the process as well as its data directory. The String default is being used
- * here to represent a catchall label when no policy matches.
+ * String retrieved from the seinfo tag found in selinux policy. This value can be set through
+ * the mac_permissions.xml policy construct. This value is used for setting an SELinux security
+ * context on the process as well as its data directory.
*
* {@hide}
*/
- public String seInfo = "default";
+ public String seInfo;
/**
* The seinfo tag generated per-user. This value may change based upon the
@@ -950,6 +957,7 @@
* Version of the sandbox the application wants to run in.
* @hide
*/
+ @SystemApi
public int targetSandboxVersion;
/**
@@ -1093,6 +1101,58 @@
/** @hide */
public String[] splitClassLoaderNames;
+ /**
+ * Represents the default policy. The actual policy used will depend on other properties of
+ * the application, e.g. the target SDK version.
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1;
+ /**
+ * No API enforcement; the app can access the entire internal private API. Only for use by
+ * system apps.
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
+ /**
+ * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
+ * black lists.
+ * @hide
+ * */
+ public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+ /**
+ * Dark grey list enforcement. Enforces the dark grey and black lists
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK = 2;
+ /**
+ * Blacklist enforcement only.
+ * @hide
+ */
+ public static final int HIDDEN_API_ENFORCEMENT_BLACK = 3;
+
+ private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_BLACK;
+
+ /**
+ * Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
+ * art/runtime/hidden_api.h
+ * @hide
+ */
+ @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
+ HIDDEN_API_ENFORCEMENT_DEFAULT,
+ HIDDEN_API_ENFORCEMENT_NONE,
+ HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+ HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
+ HIDDEN_API_ENFORCEMENT_BLACK,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface HiddenApiEnforcementPolicy {}
+
+ private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+ return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+ }
+
+ private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+
public void dump(Printer pw, String prefix) {
dump(pw, prefix, DUMP_FLAG_ALL);
}
@@ -1180,6 +1240,7 @@
if (category != CATEGORY_UNDEFINED) {
pw.println(prefix + "category=" + category);
}
+ pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
}
super.dumpBack(pw, prefix);
}
@@ -1375,6 +1436,9 @@
classLoaderName = orig.classLoaderName;
splitClassLoaderNames = orig.splitClassLoaderNames;
appComponentFactory = orig.appComponentFactory;
+ compileSdkVersion = orig.compileSdkVersion;
+ compileSdkVersionCodename = orig.compileSdkVersionCodename;
+ mHiddenApiPolicy = orig.mHiddenApiPolicy;
}
public String toString() {
@@ -1448,6 +1512,7 @@
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
dest.writeString(appComponentFactory);
+ dest.writeInt(mHiddenApiPolicy);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1518,6 +1583,7 @@
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
appComponentFactory = source.readString();
+ mHiddenApiPolicy = source.readInt();
}
/**
@@ -1588,11 +1654,31 @@
}
}
+ private boolean isPackageWhitelistedForHiddenApis() {
+ return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+ }
+
/**
* @hide
*/
- public boolean isAllowedToUseHiddenApi() {
- return isSystemApp();
+ public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+ if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+ return mHiddenApiPolicy;
+ }
+ if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
+ return HIDDEN_API_ENFORCEMENT_NONE;
+ }
+ return HIDDEN_API_ENFORCEMENT_BLACK;
+ }
+
+ /**
+ * @hide
+ */
+ public void setHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+ if (!isValidHiddenApiEnforcementPolicy(policy)) {
+ throw new IllegalArgumentException("Invalid API enforcement policy: " + policy);
+ }
+ mHiddenApiPolicy = policy;
}
/**
@@ -1647,7 +1733,11 @@
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
}
- /** @hide */
+ /**
+ * True if the application is installed as an instant app.
+ * @hide
+ */
+ @SystemApi
public boolean isInstantApp() {
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
}
@@ -1699,6 +1789,11 @@
return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
}
+ /** @hide */
+ public boolean isProduct() {
+ return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+ }
+
/**
* Returns whether or not this application was installed as a virtual preload.
*/
diff --git a/android/content/pm/AuxiliaryResolveInfo.java b/android/content/pm/AuxiliaryResolveInfo.java
index 6bdcefb..202df50 100644
--- a/android/content/pm/AuxiliaryResolveInfo.java
+++ b/android/content/pm/AuxiliaryResolveInfo.java
@@ -21,6 +21,10 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.Bundle;
+
+import java.util.Collections;
+import java.util.List;
/**
* Auxiliary application resolution response.
@@ -31,56 +35,95 @@
* hasn't been installed.
* @hide
*/
-public final class AuxiliaryResolveInfo extends IntentFilter {
- /** Resolved information returned from the external instant resolver */
- public final InstantAppResolveInfo resolveInfo;
- /** The resolved package. Copied from {@link #resolveInfo}. */
- public final String packageName;
+public final class AuxiliaryResolveInfo {
/** The activity to launch if there's an installation failure. */
public final ComponentName installFailureActivity;
- /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
- public final String splitName;
/** Whether or not instant resolution needs the second phase */
public final boolean needsPhaseTwo;
/** Opaque token to track the instant application resolution */
public final String token;
- /** The version code of the package */
- public final long versionCode;
/** An intent to start upon failure to install */
public final Intent failureIntent;
+ /** The matching filters for this resolve info. */
+ public final List<AuxiliaryFilter> filters;
/** Create a response for installing an instant application. */
- public AuxiliaryResolveInfo(@NonNull InstantAppResolveInfo resolveInfo,
- @NonNull IntentFilter orig,
- @Nullable String splitName,
- @NonNull String token,
+ public AuxiliaryResolveInfo(@NonNull String token,
boolean needsPhase2,
- @Nullable Intent failureIntent) {
- super(orig);
- this.resolveInfo = resolveInfo;
- this.packageName = resolveInfo.getPackageName();
- this.splitName = splitName;
+ @Nullable Intent failureIntent,
+ @Nullable List<AuxiliaryFilter> filters) {
this.token = token;
this.needsPhaseTwo = needsPhase2;
- this.versionCode = resolveInfo.getVersionCode();
this.failureIntent = failureIntent;
+ this.filters = filters;
this.installFailureActivity = null;
}
/** Create a response for installing a split on demand. */
- public AuxiliaryResolveInfo(@NonNull String packageName,
- @Nullable String splitName,
- @Nullable ComponentName failureActivity,
- long versionCode,
- @Nullable Intent failureIntent) {
+ public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+ @Nullable Intent failureIntent,
+ @Nullable List<AuxiliaryFilter> filters) {
super();
- this.packageName = packageName;
this.installFailureActivity = failureActivity;
- this.splitName = splitName;
- this.versionCode = versionCode;
- this.resolveInfo = null;
+ this.filters = filters;
this.token = null;
this.needsPhaseTwo = false;
this.failureIntent = failureIntent;
}
+
+ /** Create a response for installing a split on demand. */
+ public AuxiliaryResolveInfo(@Nullable ComponentName failureActivity,
+ String packageName, long versionCode, String splitName) {
+ this(failureActivity, null, Collections.singletonList(
+ new AuxiliaryResolveInfo.AuxiliaryFilter(packageName, versionCode, splitName)));
+ }
+
+ /** @hide */
+ public static final class AuxiliaryFilter extends IntentFilter {
+ /** Resolved information returned from the external instant resolver */
+ public final InstantAppResolveInfo resolveInfo;
+ /** The resolved package. Copied from {@link #resolveInfo}. */
+ public final String packageName;
+ /** The version code of the package */
+ public final long versionCode;
+ /** The resolve split. Copied from the matched filter in {@link #resolveInfo}. */
+ public final String splitName;
+ /** The extras to pass on to the installer for this filter. */
+ public final Bundle extras;
+
+ public AuxiliaryFilter(IntentFilter orig, InstantAppResolveInfo resolveInfo,
+ String splitName, Bundle extras) {
+ super(orig);
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.versionCode = resolveInfo.getLongVersionCode();
+ this.splitName = splitName;
+ this.extras = extras;
+ }
+
+ public AuxiliaryFilter(InstantAppResolveInfo resolveInfo,
+ String splitName, Bundle extras) {
+ this.resolveInfo = resolveInfo;
+ this.packageName = resolveInfo.getPackageName();
+ this.versionCode = resolveInfo.getLongVersionCode();
+ this.splitName = splitName;
+ this.extras = extras;
+ }
+
+ public AuxiliaryFilter(String packageName, long versionCode, String splitName) {
+ this.resolveInfo = null;
+ this.packageName = packageName;
+ this.versionCode = versionCode;
+ this.splitName = splitName;
+ this.extras = null;
+ }
+
+ @Override
+ public String toString() {
+ return "AuxiliaryFilter{"
+ + "packageName='" + packageName + '\''
+ + ", versionCode=" + versionCode
+ + ", splitName='" + splitName + '\'' + '}';
+ }
+ }
}
\ No newline at end of file
diff --git a/android/content/pm/EphemeralIntentFilter.java b/android/content/pm/EphemeralIntentFilter.java
deleted file mode 100644
index 1dbbf81..0000000
--- a/android/content/pm/EphemeralIntentFilter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information about an ephemeral application intent filter.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralIntentFilter implements Parcelable {
- private final InstantAppIntentFilter mInstantAppIntentFilter;
-
- public EphemeralIntentFilter(@Nullable String splitName, @NonNull List<IntentFilter> filters) {
- mInstantAppIntentFilter = new InstantAppIntentFilter(splitName, filters);
- }
-
- EphemeralIntentFilter(@NonNull InstantAppIntentFilter intentFilter) {
- mInstantAppIntentFilter = intentFilter;
- }
-
- EphemeralIntentFilter(Parcel in) {
- mInstantAppIntentFilter = in.readParcelable(null /*loader*/);
- }
-
- public String getSplitName() {
- return mInstantAppIntentFilter.getSplitName();
- }
-
- public List<IntentFilter> getFilters() {
- return mInstantAppIntentFilter.getFilters();
- }
-
- /** @hide */
- InstantAppIntentFilter getInstantAppIntentFilter() {
- return mInstantAppIntentFilter;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppIntentFilter, flags);
- }
-
- public static final Parcelable.Creator<EphemeralIntentFilter> CREATOR
- = new Parcelable.Creator<EphemeralIntentFilter>() {
- @Override
- public EphemeralIntentFilter createFromParcel(Parcel in) {
- return new EphemeralIntentFilter(in);
- }
- @Override
- public EphemeralIntentFilter[] newArray(int size) {
- return new EphemeralIntentFilter[size];
- }
- };
-}
diff --git a/android/content/pm/EphemeralResolveInfo.java b/android/content/pm/EphemeralResolveInfo.java
deleted file mode 100644
index 12131a3..0000000
--- a/android/content/pm/EphemeralResolveInfo.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2015 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 android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Information about an ephemeral application.
- * @hide
- * @removed
- */
-@Deprecated
-@SystemApi
-public final class EphemeralResolveInfo implements Parcelable {
- /** Algorithm that will be used to generate the domain digest */
- public static final String SHA_ALGORITHM = "SHA-256";
-
- private final InstantAppResolveInfo mInstantAppResolveInfo;
- @Deprecated
- private final List<IntentFilter> mLegacyFilters;
-
- @Deprecated
- public EphemeralResolveInfo(@NonNull Uri uri, @NonNull String packageName,
- @NonNull List<IntentFilter> filters) {
- if (uri == null || packageName == null || filters == null || filters.isEmpty()) {
- throw new IllegalArgumentException();
- }
- final List<EphemeralIntentFilter> ephemeralFilters = new ArrayList<>(1);
- ephemeralFilters.add(new EphemeralIntentFilter(packageName, filters));
- mInstantAppResolveInfo = new InstantAppResolveInfo(uri.getHost(), packageName,
- createInstantAppIntentFilterList(ephemeralFilters));
- mLegacyFilters = new ArrayList<IntentFilter>(filters.size());
- mLegacyFilters.addAll(filters);
- }
-
- @Deprecated
- public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters) {
- this(digest, packageName, filters, -1 /*versionCode*/);
- }
-
- public EphemeralResolveInfo(@NonNull EphemeralDigest digest, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters, int versionCode) {
- mInstantAppResolveInfo = new InstantAppResolveInfo(
- digest.getInstantAppDigest(), packageName,
- createInstantAppIntentFilterList(filters), versionCode);
- mLegacyFilters = null;
- }
-
- public EphemeralResolveInfo(@NonNull String hostName, @Nullable String packageName,
- @Nullable List<EphemeralIntentFilter> filters) {
- this(new EphemeralDigest(hostName), packageName, filters);
- }
-
- EphemeralResolveInfo(Parcel in) {
- mInstantAppResolveInfo = in.readParcelable(null /*loader*/);
- mLegacyFilters = new ArrayList<IntentFilter>();
- in.readList(mLegacyFilters, null /*loader*/);
- }
-
- /** @hide */
- public InstantAppResolveInfo getInstantAppResolveInfo() {
- return mInstantAppResolveInfo;
- }
-
- private static List<InstantAppIntentFilter> createInstantAppIntentFilterList(
- List<EphemeralIntentFilter> filters) {
- if (filters == null) {
- return null;
- }
- final int filterCount = filters.size();
- final List<InstantAppIntentFilter> returnList = new ArrayList<>(filterCount);
- for (int i = 0; i < filterCount; i++) {
- returnList.add(filters.get(i).getInstantAppIntentFilter());
- }
- return returnList;
- }
-
- public byte[] getDigestBytes() {
- return mInstantAppResolveInfo.getDigestBytes();
- }
-
- public int getDigestPrefix() {
- return mInstantAppResolveInfo.getDigestPrefix();
- }
-
- public String getPackageName() {
- return mInstantAppResolveInfo.getPackageName();
- }
-
- public List<EphemeralIntentFilter> getIntentFilters() {
- final List<InstantAppIntentFilter> filters = mInstantAppResolveInfo.getIntentFilters();
- final int filterCount = filters.size();
- final List<EphemeralIntentFilter> returnList = new ArrayList<>(filterCount);
- for (int i = 0; i < filterCount; i++) {
- returnList.add(new EphemeralIntentFilter(filters.get(i)));
- }
- return returnList;
- }
-
- public int getVersionCode() {
- return mInstantAppResolveInfo.getVersionCode();
- }
-
- @Deprecated
- public List<IntentFilter> getFilters() {
- return mLegacyFilters;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppResolveInfo, flags);
- out.writeList(mLegacyFilters);
- }
-
- public static final Parcelable.Creator<EphemeralResolveInfo> CREATOR
- = new Parcelable.Creator<EphemeralResolveInfo>() {
- @Override
- public EphemeralResolveInfo createFromParcel(Parcel in) {
- return new EphemeralResolveInfo(in);
- }
- @Override
- public EphemeralResolveInfo[] newArray(int size) {
- return new EphemeralResolveInfo[size];
- }
- };
-
- /**
- * Helper class to generate and store each of the digests and prefixes
- * sent to the Ephemeral Resolver.
- * <p>
- * Since intent filters may want to handle multiple hosts within a
- * domain [eg “*.google.com”], the resolver is presented with multiple
- * hash prefixes. For example, "a.b.c.d.e" generates digests for
- * "d.e", "c.d.e", "b.c.d.e" and "a.b.c.d.e".
- *
- * @hide
- */
- @SystemApi
- public static final class EphemeralDigest implements Parcelable {
- private final InstantAppDigest mInstantAppDigest;
-
- public EphemeralDigest(@NonNull String hostName) {
- this(hostName, -1 /*maxDigests*/);
- }
-
- /** @hide */
- public EphemeralDigest(@NonNull String hostName, int maxDigests) {
- mInstantAppDigest = new InstantAppDigest(hostName, maxDigests);
- }
-
- EphemeralDigest(Parcel in) {
- mInstantAppDigest = in.readParcelable(null /*loader*/);
- }
-
- /** @hide */
- InstantAppDigest getInstantAppDigest() {
- return mInstantAppDigest;
- }
-
- public byte[][] getDigestBytes() {
- return mInstantAppDigest.getDigestBytes();
- }
-
- public int[] getDigestPrefix() {
- return mInstantAppDigest.getDigestPrefix();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeParcelable(mInstantAppDigest, flags);
- }
-
- @SuppressWarnings("hiding")
- public static final Parcelable.Creator<EphemeralDigest> CREATOR =
- new Parcelable.Creator<EphemeralDigest>() {
- @Override
- public EphemeralDigest createFromParcel(Parcel in) {
- return new EphemeralDigest(in);
- }
- @Override
- public EphemeralDigest[] newArray(int size) {
- return new EphemeralDigest[size];
- }
- };
- }
-}
diff --git a/android/content/pm/InstantAppRequest.java b/android/content/pm/InstantAppRequest.java
index 38f0225..361d4e4 100644
--- a/android/content/pm/InstantAppRequest.java
+++ b/android/content/pm/InstantAppRequest.java
@@ -18,12 +18,14 @@
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
/**
* Information needed to make an instant application resolution request.
* @hide
*/
public final class InstantAppRequest {
+
/** Response from the first phase of instant application resolution */
public final AuxiliaryResolveInfo responseObj;
/** The original intent that triggered instant application resolution */
@@ -40,6 +42,8 @@
public final Bundle verificationBundle;
/** Whether resolution occurs because an application is starting */
public final boolean resolveForStart;
+ /** The instant app digest for this request */
+ public final InstantAppResolveInfo.InstantAppDigest digest;
public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
String resolvedType, String callingPackage, int userId, Bundle verificationBundle,
@@ -51,5 +55,11 @@
this.userId = userId;
this.verificationBundle = verificationBundle;
this.resolveForStart = resolveForStart;
+ if (origIntent.getData() != null && !TextUtils.isEmpty(origIntent.getData().getHost())) {
+ digest = new InstantAppResolveInfo.InstantAppDigest(
+ origIntent.getData().getHost(), 5 /*maxDigests*/);
+ } else {
+ digest = InstantAppResolveInfo.InstantAppDigest.UNDEFINED;
+ }
}
}
diff --git a/android/content/pm/InstantAppResolveInfo.java b/android/content/pm/InstantAppResolveInfo.java
index 19cb932..8184361 100644
--- a/android/content/pm/InstantAppResolveInfo.java
+++ b/android/content/pm/InstantAppResolveInfo.java
@@ -19,18 +19,46 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Random;
/**
- * Information about an instant application.
+ * Describes an externally resolvable instant application. There are three states that this class
+ * can represent: <p/>
+ * <ul>
+ * <li>
+ * The first, usable only for non http/s intents, implies that the resolver cannot
+ * immediately resolve this intent and would prefer that resolution be deferred to the
+ * instant app installer. Represent this state with {@link #InstantAppResolveInfo(Bundle)}.
+ * If the {@link android.content.Intent} has the scheme set to http/s and a set of digest
+ * prefixes were passed into one of the resolve methods in
+ * {@link android.app.InstantAppResolverService}, this state cannot be used.
+ * </li>
+ * <li>
+ * The second represents a partial match and is constructed with any of the other
+ * constructors. By setting one or more of the {@link Nullable}arguments to null, you
+ * communicate to the resolver in response to
+ * {@link android.app.InstantAppResolverService#onGetInstantAppResolveInfo(Intent, int[],
+ * String, InstantAppResolverService.InstantAppResolutionCallback)}
+ * that you need a 2nd round of resolution to complete the request.
+ * </li>
+ * <li>
+ * The third represents a complete match and is constructed with all @Nullable parameters
+ * populated.
+ * </li>
+ * </ul>
* @hide
*/
@SystemApi
@@ -38,6 +66,8 @@
/** Algorithm that will be used to generate the domain digest */
private static final String SHA_ALGORITHM = "SHA-256";
+ private static final byte[] EMPTY_DIGEST = new byte[0];
+
private final InstantAppDigest mDigest;
private final String mPackageName;
/** The filters used to match domain */
@@ -46,15 +76,43 @@
private final long mVersionCode;
/** Data about the app that should be passed along to the Instant App installer on resolve */
private final Bundle mExtras;
+ /**
+ * A flag that indicates that the resolver is aware that an app may match, but would prefer
+ * that the installer get the sanitized intent to decide.
+ */
+ private final boolean mShouldLetInstallerDecide;
+ /** Constructor for intent-based InstantApp resolution results. */
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters, int versionCode) {
this(digest, packageName, filters, (long) versionCode, null /* extras */);
}
+ /** Constructor for intent-based InstantApp resolution results with extras. */
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
@Nullable List<InstantAppIntentFilter> filters, long versionCode,
@Nullable Bundle extras) {
+ this(digest, packageName, filters, versionCode, extras, false);
+ }
+
+ /** Constructor for intent-based InstantApp resolution results by hostname. */
+ public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters) {
+ this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+ null /* extras */);
+ }
+
+ /**
+ * Constructor that indicates that resolution could be delegated to the installer when the
+ * sanitized intent contains enough information to resolve completely.
+ */
+ public InstantAppResolveInfo(@Nullable Bundle extras) {
+ this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true);
+ }
+
+ private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+ @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+ @Nullable Bundle extras, boolean shouldLetInstallerDecide) {
// validate arguments
if ((packageName == null && (filters != null && filters.size() != 0))
|| (packageName != null && (filters == null || filters.size() == 0))) {
@@ -62,7 +120,7 @@
}
mDigest = digest;
if (filters != null) {
- mFilters = new ArrayList<InstantAppIntentFilter>(filters.size());
+ mFilters = new ArrayList<>(filters.size());
mFilters.addAll(filters);
} else {
mFilters = null;
@@ -70,25 +128,37 @@
mPackageName = packageName;
mVersionCode = versionCode;
mExtras = extras;
- }
-
- public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
- @Nullable List<InstantAppIntentFilter> filters) {
- this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
- null /* extras */);
+ mShouldLetInstallerDecide = shouldLetInstallerDecide;
}
InstantAppResolveInfo(Parcel in) {
- mDigest = in.readParcelable(null /*loader*/);
- mPackageName = in.readString();
- mFilters = new ArrayList<InstantAppIntentFilter>();
- in.readList(mFilters, null /*loader*/);
- mVersionCode = in.readLong();
+ mShouldLetInstallerDecide = in.readBoolean();
mExtras = in.readBundle();
+ if (mShouldLetInstallerDecide) {
+ mDigest = InstantAppDigest.UNDEFINED;
+ mPackageName = null;
+ mFilters = Collections.emptyList();
+ mVersionCode = -1;
+ } else {
+ mDigest = in.readParcelable(null /*loader*/);
+ mPackageName = in.readString();
+ mFilters = new ArrayList<>();
+ in.readList(mFilters, null /*loader*/);
+ mVersionCode = in.readLong();
+ }
+ }
+
+ /**
+ * Returns true if the resolver is aware that an app may match, but would prefer
+ * that the installer get the sanitized intent to decide. This should not be true for
+ * resolutions that include a host and will be ignored in such cases.
+ */
+ public boolean shouldLetInstallerDecide() {
+ return mShouldLetInstallerDecide;
}
public byte[] getDigestBytes() {
- return mDigest.getDigestBytes()[0];
+ return mDigest.mDigestBytes.length > 0 ? mDigest.getDigestBytes()[0] : EMPTY_DIGEST;
}
public int getDigestPrefix() {
@@ -127,11 +197,15 @@
@Override
public void writeToParcel(Parcel out, int flags) {
+ out.writeBoolean(mShouldLetInstallerDecide);
+ out.writeBundle(mExtras);
+ if (mShouldLetInstallerDecide) {
+ return;
+ }
out.writeParcelable(mDigest, flags);
out.writeString(mPackageName);
out.writeList(mFilters);
out.writeLong(mVersionCode);
- out.writeBundle(mExtras);
}
public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
@@ -158,12 +232,30 @@
*/
@SystemApi
public static final class InstantAppDigest implements Parcelable {
- private static final int DIGEST_MASK = 0xfffff000;
- private static final int DIGEST_PREFIX_COUNT = 5;
+ static final int DIGEST_MASK = 0xfffff000;
+
+ /**
+ * A special instance that represents and undefined digest used for cases that a host was
+ * not provided or is irrelevant to the response.
+ */
+ public static final InstantAppDigest UNDEFINED =
+ new InstantAppDigest(new byte[][]{}, new int[]{});
+
+ private static Random sRandom = null;
+ static {
+ try {
+ sRandom = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ // oh well
+ sRandom = new Random();
+ }
+ }
/** Full digest of the domain hashes */
private final byte[][] mDigestBytes;
- /** The first 4 bytes of the domain hashes */
+ /** The first 5 bytes of the domain hashes */
private final int[] mDigestPrefix;
+ /** The first 5 bytes of the domain hashes interspersed with random data */
+ private int[] mDigestPrefixSecure;
public InstantAppDigest(@NonNull String hostName) {
this(hostName, -1 /*maxDigests*/);
@@ -186,6 +278,11 @@
}
}
+ private InstantAppDigest(byte[][] digestBytes, int[] prefix) {
+ this.mDigestPrefix = prefix;
+ this.mDigestBytes = digestBytes;
+ }
+
private static byte[][] generateDigest(String hostName, int maxDigests) {
ArrayList<byte[]> digests = new ArrayList<>();
try {
@@ -230,6 +327,7 @@
}
}
mDigestPrefix = in.createIntArray();
+ mDigestPrefixSecure = in.createIntArray();
}
public byte[][] getDigestBytes() {
@@ -240,6 +338,26 @@
return mDigestPrefix;
}
+ /**
+ * Returns a digest prefix with additional random prefixes interspersed.
+ * @hide
+ */
+ public int[] getDigestPrefixSecure() {
+ if (this == InstantAppResolveInfo.InstantAppDigest.UNDEFINED) {
+ return getDigestPrefix();
+ } else if (mDigestPrefixSecure == null) {
+ // let's generate some random data to intersperse throughout the set of prefixes
+ final int realSize = getDigestPrefix().length;
+ final int manufacturedSize = realSize + 10 + sRandom.nextInt(10);
+ mDigestPrefixSecure = Arrays.copyOf(getDigestPrefix(), manufacturedSize);
+ for (int i = realSize; i < manufacturedSize; i++) {
+ mDigestPrefixSecure[i] = sRandom.nextInt() & DIGEST_MASK;
+ }
+ Arrays.sort(mDigestPrefixSecure);
+ }
+ return mDigestPrefixSecure;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -247,6 +365,11 @@
@Override
public void writeToParcel(Parcel out, int flags) {
+ final boolean isUndefined = this == UNDEFINED;
+ out.writeBoolean(isUndefined);
+ if (isUndefined) {
+ return;
+ }
if (mDigestBytes == null) {
out.writeInt(-1);
} else {
@@ -256,6 +379,7 @@
}
}
out.writeIntArray(mDigestPrefix);
+ out.writeIntArray(mDigestPrefixSecure);
}
@SuppressWarnings("hiding")
@@ -263,6 +387,9 @@
new Parcelable.Creator<InstantAppDigest>() {
@Override
public InstantAppDigest createFromParcel(Parcel in) {
+ if (in.readBoolean() /* is undefined */) {
+ return UNDEFINED;
+ }
return new InstantAppDigest(in);
}
@Override
diff --git a/android/content/pm/LauncherApps.java b/android/content/pm/LauncherApps.java
index b4a7eec..9aace2e 100644
--- a/android/content/pm/LauncherApps.java
+++ b/android/content/pm/LauncherApps.java
@@ -211,6 +211,10 @@
* example, this can happen when a Device Administrator suspends
* an applicaton.
*
+ * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
+ * any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will
+ * not receive this callback.
+ *
* @param packageNames The names of the packages that have just been
* suspended.
* @param user The UserHandle of the profile that generated the change.
@@ -219,6 +223,22 @@
}
/**
+ * Indicates that one or more packages have been suspended. A device administrator or an app
+ * with {@code android.permission.SUSPEND_APPS} can do this.
+ *
+ * @param packageNames The names of the packages that have just been suspended.
+ * @param launcherExtras A {@link Bundle} of extras for the launcher.
+ * @param user the user for which the given packages were suspended.
+ *
+ * @see PackageManager#isPackageSuspended()
+ * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
+ */
+ public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras,
+ UserHandle user) {
+ onPackagesSuspended(packageNames, user);
+ }
+
+ /**
* Indicates that one or more packages have been unsuspended. For
* example, this can happen when a Device Administrator unsuspends
* an applicaton.
@@ -638,6 +658,31 @@
}
/**
+ * Gets the launcher extras supplied to the system when the given package was suspended via
+ * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)}.
+ *
+ * <p>Note: This just returns whatever extras were provided to the system, <em>which might
+ * even be {@code null}.</em>
+ *
+ * @param packageName The package for which to fetch the launcher extras.
+ * @param user The {@link UserHandle} of the profile.
+ * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
+ * suspended.
+ *
+ * @see Callback#onPackagesSuspended(String[], Bundle, UserHandle)
+ * @see PackageManager#isPackageSuspended()
+ */
+ public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
+ logErrorForInvalidProfileAccess(user);
+ try {
+ return mService.getSuspendedPackageLauncherExtras(packageName, user);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns {@link ApplicationInfo} about an application installed for a specific user profile.
*
* @param packageName The package name of the application
@@ -652,7 +697,7 @@
@ApplicationInfoFlags int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
Preconditions.checkNotNull(packageName, "packageName");
- Preconditions.checkNotNull(packageName, "user");
+ Preconditions.checkNotNull(user, "user");
logErrorForInvalidProfileAccess(user);
try {
final ApplicationInfo ai = mService
@@ -1163,14 +1208,15 @@
}
@Override
- public void onPackagesSuspended(UserHandle user, String[] packageNames)
+ public void onPackagesSuspended(UserHandle user, String[] packageNames,
+ Bundle launcherExtras)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
- callback.postOnPackagesSuspended(packageNames, user);
+ callback.postOnPackagesSuspended(packageNames, launcherExtras, user);
}
}
}
@@ -1218,6 +1264,7 @@
private static class CallbackInfo {
String[] packageNames;
String packageName;
+ Bundle launcherExtras;
boolean replacing;
UserHandle user;
List<ShortcutInfo> shortcuts;
@@ -1251,7 +1298,8 @@
mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
break;
case MSG_SUSPENDED:
- mCallback.onPackagesSuspended(info.packageNames, info.user);
+ mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras,
+ info.user);
break;
case MSG_UNSUSPENDED:
mCallback.onPackagesUnsuspended(info.packageNames, info.user);
@@ -1301,10 +1349,12 @@
obtainMessage(MSG_UNAVAILABLE, info).sendToTarget();
}
- public void postOnPackagesSuspended(String[] packageNames, UserHandle user) {
+ public void postOnPackagesSuspended(String[] packageNames, Bundle launcherExtras,
+ UserHandle user) {
CallbackInfo info = new CallbackInfo();
info.packageNames = packageNames;
info.user = user;
+ info.launcherExtras = launcherExtras;
obtainMessage(MSG_SUSPENDED, info).sendToTarget();
}
@@ -1429,6 +1479,10 @@
* Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
* different request type.
*
+ * <p>Launcher should not show any configuration activity associated with the provider, and
+ * assume that the widget is already fully configured. Upon accepting the widget, it should
+ * pass the widgetId in {@link #accept(Bundle)}.
+ *
* @return requested {@link AppWidgetProviderInfo} when a request is of the
* {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise.
*/
diff --git a/android/content/pm/OrgApacheHttpLegacyUpdater.java b/android/content/pm/OrgApacheHttpLegacyUpdater.java
index 81041e9..81e4105 100644
--- a/android/content/pm/OrgApacheHttpLegacyUpdater.java
+++ b/android/content/pm/OrgApacheHttpLegacyUpdater.java
@@ -15,13 +15,12 @@
*/
package android.content.pm;
+import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
import android.content.pm.PackageParser.Package;
-import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
-import java.util.ArrayList;
-
/**
* Updates a package to ensure that if it targets < P that the org.apache.http.legacy library is
* included by default.
@@ -37,30 +36,13 @@
@VisibleForTesting
public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater {
- private static final String APACHE_HTTP_LEGACY = "org.apache.http.legacy";
-
@Override
public void updatePackage(Package pkg) {
- ArrayList<String> usesLibraries = pkg.usesLibraries;
- ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
// Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
// to be accessible so this maintains backward compatibility by adding the
// org.apache.http.legacy library to those packages.
if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) {
- boolean apacheHttpLegacyPresent = isLibraryPresent(
- usesLibraries, usesOptionalLibraries, APACHE_HTTP_LEGACY);
- if (!apacheHttpLegacyPresent) {
- usesLibraries = prefix(usesLibraries, APACHE_HTTP_LEGACY);
- }
+ prefixRequiredLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
}
-
- pkg.usesLibraries = usesLibraries;
- pkg.usesOptionalLibraries = usesOptionalLibraries;
- }
-
- private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) {
- int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
- return targetSdkVersion <= Build.VERSION_CODES.O_MR1;
}
}
diff --git a/android/content/pm/PackageBackwardCompatibility.java b/android/content/pm/PackageBackwardCompatibility.java
index 9bdb78b..a16f81b 100644
--- a/android/content/pm/PackageBackwardCompatibility.java
+++ b/android/content/pm/PackageBackwardCompatibility.java
@@ -16,14 +16,19 @@
package android.content.pm;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+
import android.content.pm.PackageParser.Package;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Supplier;
/**
* Modifies {@link Package} in order to maintain backwards compatibility.
@@ -35,54 +40,90 @@
private static final String TAG = PackageBackwardCompatibility.class.getSimpleName();
- private static final String ANDROID_TEST_MOCK = "android.test.mock";
-
- private static final String ANDROID_TEST_RUNNER = "android.test.runner";
-
private static final PackageBackwardCompatibility INSTANCE;
static {
- String className = "android.content.pm.OrgApacheHttpLegacyUpdater";
+ final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
+
+ // Attempt to load and add the optional updater that will only be available when
+ // REMOVE_OAHL_FROM_BCP=true. If that could not be found then add the default updater that
+ // will remove any references to org.apache.http.library from the package so that it does
+ // not try and load the library when it is on the bootclasspath.
+ boolean bootClassPathContainsOAHL = !addOptionalUpdater(packageUpdaters,
+ "android.content.pm.OrgApacheHttpLegacyUpdater",
+ RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new);
+
+ // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
+ // android.test.mock.
+ packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
+
+ // Attempt to load and add the optional updater that will only be available when
+ // REMOVE_ATB_FROM_BCP=true. If that could not be found then add the default updater that
+ // will remove any references to org.apache.http.library from the package so that it does
+ // not try and load the library when it is on the bootclasspath.
+ boolean bootClassPathContainsATB = !addOptionalUpdater(packageUpdaters,
+ "android.content.pm.AndroidTestBaseUpdater",
+ RemoveUnnecessaryAndroidTestBaseLibrary::new);
+
+ PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
+ .toArray(new PackageSharedLibraryUpdater[0]);
+ INSTANCE = new PackageBackwardCompatibility(
+ bootClassPathContainsOAHL, bootClassPathContainsATB, updaterArray);
+ }
+
+ /**
+ * Add an optional {@link PackageSharedLibraryUpdater} instance to the list, if it could not be
+ * found then add a default instance instead.
+ *
+ * @param packageUpdaters the list to update.
+ * @param className the name of the optional class.
+ * @param defaultUpdater the supplier of the default instance.
+ * @return true if the optional updater was added false otherwise.
+ */
+ private static boolean addOptionalUpdater(List<PackageSharedLibraryUpdater> packageUpdaters,
+ String className, Supplier<PackageSharedLibraryUpdater> defaultUpdater) {
Class<? extends PackageSharedLibraryUpdater> clazz;
try {
clazz = (PackageBackwardCompatibility.class.getClassLoader()
.loadClass(className)
.asSubclass(PackageSharedLibraryUpdater.class));
+ Log.i(TAG, "Loaded " + className);
} catch (ClassNotFoundException e) {
Log.i(TAG, "Could not find " + className + ", ignoring");
clazz = null;
}
- boolean hasOrgApacheHttpLegacy = false;
- final List<PackageSharedLibraryUpdater> packageUpdaters = new ArrayList<>();
+ boolean usedOptional = false;
+ PackageSharedLibraryUpdater updater;
if (clazz == null) {
- // Add an updater that will remove any references to org.apache.http.library from the
- // package so that it does not try and load the library when it is on the
- // bootclasspath.
- packageUpdaters.add(new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
+ updater = defaultUpdater.get();
} else {
try {
- packageUpdaters.add(clazz.getConstructor().newInstance());
- hasOrgApacheHttpLegacy = true;
+ updater = clazz.getConstructor().newInstance();
+ usedOptional = true;
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Could not create instance of " + className, e);
}
}
-
- packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
-
- PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
- .toArray(new PackageSharedLibraryUpdater[0]);
- INSTANCE = new PackageBackwardCompatibility(hasOrgApacheHttpLegacy, updaterArray);
+ packageUpdaters.add(updater);
+ return usedOptional;
}
- private final boolean mRemovedOAHLFromBCP;
+ @VisibleForTesting
+ public static PackageSharedLibraryUpdater getInstance() {
+ return INSTANCE;
+ }
+
+ private final boolean mBootClassPathContainsOAHL;
+
+ private final boolean mBootClassPathContainsATB;
private final PackageSharedLibraryUpdater[] mPackageUpdaters;
- public PackageBackwardCompatibility(boolean removedOAHLFromBCP,
- PackageSharedLibraryUpdater[] packageUpdaters) {
- this.mRemovedOAHLFromBCP = removedOAHLFromBCP;
+ public PackageBackwardCompatibility(boolean bootClassPathContainsOAHL,
+ boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
+ this.mBootClassPathContainsOAHL = bootClassPathContainsOAHL;
+ this.mBootClassPathContainsATB = bootClassPathContainsATB;
this.mPackageUpdaters = packageUpdaters;
}
@@ -99,17 +140,25 @@
@Override
public void updatePackage(Package pkg) {
-
for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
packageUpdater.updatePackage(pkg);
}
}
/**
- * True if the org.apache.http.legacy has been removed the bootclasspath, false otherwise.
+ * True if the org.apache.http.legacy is on the bootclasspath, false otherwise.
*/
- public static boolean removeOAHLFromBCP() {
- return INSTANCE.mRemovedOAHLFromBCP;
+ @VisibleForTesting
+ public static boolean bootClassPathContainsOAHL() {
+ return INSTANCE.mBootClassPathContainsOAHL;
+ }
+
+ /**
+ * True if the android.test.base is on the bootclasspath, false otherwise.
+ */
+ @VisibleForTesting
+ public static boolean bootClassPathContainsATB() {
+ return INSTANCE.mBootClassPathContainsATB;
}
/**
@@ -126,24 +175,9 @@
@Override
public void updatePackage(Package pkg) {
- ArrayList<String> usesLibraries = pkg.usesLibraries;
- ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
-
// android.test.runner has a dependency on android.test.mock so if android.test.runner
// is present but android.test.mock is not then add android.test.mock.
- boolean androidTestMockPresent = isLibraryPresent(
- usesLibraries, usesOptionalLibraries, ANDROID_TEST_MOCK);
- if (ArrayUtils.contains(usesLibraries, ANDROID_TEST_RUNNER)
- && !androidTestMockPresent) {
- usesLibraries.add(ANDROID_TEST_MOCK);
- }
- if (ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_RUNNER)
- && !androidTestMockPresent) {
- usesOptionalLibraries.add(ANDROID_TEST_MOCK);
- }
-
- pkg.usesLibraries = usesLibraries;
- pkg.usesOptionalLibraries = usesOptionalLibraries;
+ prefixImplicitDependency(pkg, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
}
}
@@ -155,13 +189,24 @@
public static class RemoveUnnecessaryOrgApacheHttpLegacyLibrary
extends PackageSharedLibraryUpdater {
- private static final String APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+ @Override
+ public void updatePackage(Package pkg) {
+ removeLibrary(pkg, ORG_APACHE_HTTP_LEGACY);
+ }
+
+ }
+
+ /**
+ * Remove any usages of android.test.base from the shared library as the library is on the
+ * bootclasspath.
+ */
+ @VisibleForTesting
+ public static class RemoveUnnecessaryAndroidTestBaseLibrary
+ extends PackageSharedLibraryUpdater {
@Override
public void updatePackage(Package pkg) {
- pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, APACHE_HTTP_LEGACY);
- pkg.usesOptionalLibraries =
- ArrayUtils.remove(pkg.usesOptionalLibraries, APACHE_HTTP_LEGACY);
+ removeLibrary(pkg, ANDROID_TEST_BASE);
}
}
}
diff --git a/android/content/pm/PackageInfo.java b/android/content/pm/PackageInfo.java
index 09a46b8..627ceb7 100644
--- a/android/content/pm/PackageInfo.java
+++ b/android/content/pm/PackageInfo.java
@@ -362,6 +362,13 @@
*/
public String overlayTarget;
+ /**
+ * The overlay category, if any, of this package
+ *
+ * @hide
+ */
+ public String overlayCategory;
+
/** @hide */
public int overlayPriority;
@@ -464,10 +471,23 @@
dest.writeString(restrictedAccountType);
dest.writeString(requiredAccountType);
dest.writeString(overlayTarget);
+ dest.writeString(overlayCategory);
dest.writeInt(overlayPriority);
dest.writeBoolean(mOverlayIsStatic);
dest.writeInt(compileSdkVersion);
dest.writeString(compileSdkVersionCodename);
+ writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
+ }
+
+ private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
+ if (signingCertificateHistory != null) {
+ dest.writeInt(signingCertificateHistory.length);
+ for (int i = 0; i < signingCertificateHistory.length; i++) {
+ dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
+ }
+ } else {
+ dest.writeInt(-1);
+ }
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -519,10 +539,12 @@
restrictedAccountType = source.readString();
requiredAccountType = source.readString();
overlayTarget = source.readString();
+ overlayCategory = source.readString();
overlayPriority = source.readInt();
mOverlayIsStatic = source.readBoolean();
compileSdkVersion = source.readInt();
compileSdkVersionCodename = source.readString();
+ readSigningCertificateHistoryFromParcel(source);
// The component lists were flattened with the redundant ApplicationInfo
// instances omitted. Distribute the canonical one here as appropriate.
@@ -534,6 +556,16 @@
}
}
+ private void readSigningCertificateHistoryFromParcel(Parcel source) {
+ int len = source.readInt();
+ if (len != -1) {
+ signingCertificateHistory = new Signature[len][];
+ for (int i = 0; i < len; i++) {
+ signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
+ }
+ }
+ }
+
private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
if (components != null) {
for (ComponentInfo ci : components) {
diff --git a/android/content/pm/PackageInstaller.java b/android/content/pm/PackageInstaller.java
index df677d2..25af1a7 100644
--- a/android/content/pm/PackageInstaller.java
+++ b/android/content/pm/PackageInstaller.java
@@ -448,11 +448,17 @@
/**
* Uninstall the given package, removing it completely from the device. This
- * method is only available to the current "installer of record" for the
- * package.
+ * method is available to:
+ * <ul>
+ * <li>the current "installer of record" for the package
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * </ul>
*
* @param packageName The package to uninstall.
* @param statusReceiver Where to deliver the result.
+ *
+ * @see android.app.admin.DevicePolicyManager
*/
@RequiresPermission(anyOf = {
Manifest.permission.DELETE_PACKAGES,
@@ -480,14 +486,22 @@
/**
* Uninstall the given package with a specific version code, removing it
- * completely from the device. This method is only available to the current
- * "installer of record" for the package. If the version code of the package
+ * completely from the device. If the version code of the package
* does not match the one passed in the versioned package argument this
* method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
* uninstall the latest version of the package.
+ * <p>
+ * This method is available to:
+ * <ul>
+ * <li>the current "installer of record" for the package
+ * <li>the device owner
+ * <li>the affiliated profile owner
+ * </ul>
*
* @param versionedPackage The versioned package to uninstall.
* @param statusReceiver Where to deliver the result.
+ *
+ * @see android.app.admin.DevicePolicyManager
*/
@RequiresPermission(anyOf = {
Manifest.permission.DELETE_PACKAGES,
@@ -829,7 +843,19 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+ }
+ /** {@hide} */
+ public void write(@NonNull String name, long offsetBytes, long lengthBytes,
+ @NonNull ParcelFileDescriptor fd) throws IOException {
+ try {
+ mSession.write(name, offsetBytes, lengthBytes, fd);
+ } catch (RuntimeException e) {
+ ExceptionUtils.maybeUnwrapIOException(e);
+ throw e;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -929,9 +955,14 @@
* Once this method is called, the session is sealed and no additional
* mutations may be performed on the session. If the device reboots
* before the session has been finalized, you may commit the session again.
+ * <p>
+ * If the installer is the device owner or the affiliated profile owner, there will be no
+ * user intervention.
*
* @throws SecurityException if streams opened through
* {@link #openWrite(String, long, long)} are still open.
+ *
+ * @see android.app.admin.DevicePolicyManager
*/
public void commit(@NonNull IntentSender statusReceiver) {
try {
diff --git a/android/content/pm/PackageItemInfo.java b/android/content/pm/PackageItemInfo.java
index 2c0c6ad..53ffd55 100644
--- a/android/content/pm/PackageItemInfo.java
+++ b/android/content/pm/PackageItemInfo.java
@@ -43,6 +43,14 @@
*/
public class PackageItemInfo {
private static final float MAX_LABEL_SIZE_PX = 500f;
+
+ private static volatile boolean sForceSafeLabels = false;
+
+ /** {@hide} */
+ public static void setForceSafeLabels(boolean forceSafeLabels) {
+ sForceSafeLabels = forceSafeLabels;
+ }
+
/**
* Public name of this item. From the "android:name" attribute.
*/
@@ -128,7 +136,16 @@
* @return Returns a CharSequence containing the item's label. If the
* item does not have a label, its name is returned.
*/
- public CharSequence loadLabel(PackageManager pm) {
+ public @NonNull CharSequence loadLabel(@NonNull PackageManager pm) {
+ if (sForceSafeLabels) {
+ return loadSafeLabel(pm);
+ } else {
+ return loadUnsafeLabel(pm);
+ }
+ }
+
+ /** {@hide} */
+ public CharSequence loadUnsafeLabel(PackageManager pm) {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
@@ -163,7 +180,7 @@
@SystemApi
public @NonNull CharSequence loadSafeLabel(@NonNull PackageManager pm) {
// loadLabel() always returns non-null
- String label = loadLabel(pm).toString();
+ String label = loadUnsafeLabel(pm).toString();
// strip HTML tags to avoid <br> and other tags overwriting original message
String labelStr = Html.fromHtml(label).toString();
diff --git a/android/content/pm/PackageManager.java b/android/content/pm/PackageManager.java
index df69d80..491f0af 100644
--- a/android/content/pm/PackageManager.java
+++ b/android/content/pm/PackageManager.java
@@ -51,6 +51,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -441,6 +442,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static final int MATCH_FACTORY_ONLY = 0x00200000;
/**
@@ -1950,6 +1952,14 @@
* <li>Minor version number in bits 21-12</li>
* <li>Patch version number in bits 11-0</li>
* </ul>
+ * A version of 1.1.0 or higher also indicates:
+ * <ul>
+ * <li>The {@code VK_ANDROID_external_memory_android_hardware_buffer} extension is
+ * supported.</li>
+ * <li>{@code SYNC_FD} external semaphore and fence handles are supported.</li>
+ * <li>{@code VkPhysicalDeviceSamplerYcbcrConversionFeatures::samplerYcbcrConversion} is
+ * supported.</li>
+ * </ul>
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
@@ -2061,6 +2071,15 @@
"android.hardware.sensor.hifi_sensors";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports a hardware mechanism for invoking an assist gesture.
+ * @see android.provider.Settings.Secure#ASSIST_GESTURE_ENABLED
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_ASSIST_GESTURE = "android.hardware.sensor.assist";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has a telephony radio with data
* communication support.
@@ -2099,8 +2118,6 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
* supports embedded subscriptions on eUICCs.
- * TODO(b/35851809): Make this public.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_EUICC = "android.hardware.telephony.euicc";
@@ -2604,6 +2621,14 @@
public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device has a StrongBox hardware-backed Keystore.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_STRONGBOX_KEYSTORE =
+ "android.hardware.strongbox_keystore";
+
+ /**
* Action to external storage service to clean out removed apps.
* @hide
*/
@@ -2949,6 +2974,11 @@
*/
public static final int VERSION_CODE_HIGHEST = -1;
+ /** {@hide} */
+ public int getUserId() {
+ return UserHandle.myUserId();
+ }
+
/**
* Retrieve overall information about an application package that is
* installed on the system.
@@ -3681,6 +3711,7 @@
*
* @hide
*/
+ @TestApi
public abstract @Nullable String[] getNamesForUids(int[] uids);
/**
@@ -4154,6 +4185,12 @@
public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
/**
+ * @hide
+ */
+ public abstract ResolveInfo resolveServiceAsUser(Intent intent, @ResolveInfoFlags int flags,
+ @UserIdInt int userId);
+
+ /**
* Retrieve all services that can match the given intent.
*
* @param intent The desired intent as per resolveService().
@@ -4750,7 +4787,7 @@
PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
if ((flags & GET_SIGNATURES) != 0) {
- PackageParser.collectCertificates(pkg, 0);
+ PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
PackageUserState state = new PackageUserState();
return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
@@ -5045,6 +5082,7 @@
* which market the package came from.
*
* @param packageName The name of the package to query
+ * @throws IllegalArgumentException if the given package name is not installed
*/
public abstract String getInstallerPackageName(String packageName);
@@ -5196,7 +5234,7 @@
*/
@Deprecated
public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
- getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
+ getPackageSizeInfoAsUser(packageName, getUserId(), observer);
}
/**
@@ -5474,28 +5512,49 @@
/**
* Puts the package in a suspended state, where attempts at starting activities are denied.
*
- * <p>It doesn't remove the data or the actual package file. The application notifications
- * will be hidden, the application will not show up in recents, will not be able to show
- * toasts or dialogs or ring the device.
+ * <p>It doesn't remove the data or the actual package file. The application's notifications
+ * will be hidden, any of its started activities will be stopped and it will not be able to
+ * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
+ * system dialog with the given {@code dialogMessage} will be shown instead.</p>
*
* <p>The package must already be installed. If the package is uninstalled while suspended
- * the package will no longer be suspended.
+ * the package will no longer be suspended. </p>
+ *
+ * <p>Optionally, the suspending app can provide extra information in the form of
+ * {@link PersistableBundle} objects to be shared with the apps being suspended and the
+ * launcher to support customization that they might need to handle the suspended state. </p>
+ *
+ * <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} or
+ * {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true} than the packages will be suspended, if set to
- * {@code false} the packages will be unsuspended.
- * @param userId The user id.
+ * {@code false}, the packages will be unsuspended.
+ * @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
+ * which will be shared with the apps being suspended. Ignored if
+ * {@code suspended} is false.
+ * @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
+ * provide which will be shared with the launcher. Ignored if
+ * {@code suspended} is false.
+ * @param dialogMessage The message to be displayed to the user, when they try to launch a
+ * suspended app.
*
* @return an array of package names for which the suspended status is not set as requested in
* this method.
*
* @hide
*/
- public abstract String[] setPackagesSuspendedAsUser(
- String[] packageNames, boolean suspended, @UserIdInt int userId);
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.SUSPEND_APPS,
+ Manifest.permission.MANAGE_USERS})
+ public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
+ @Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
+ String dialogMessage) {
+ throw new UnsupportedOperationException("setPackagesSuspended not implemented");
+ }
/**
- * @see #setPackageSuspendedAsUser(String, boolean, int)
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
* @param packageName The name of the package to get the suspended status of.
* @param userId The user id.
* @return {@code true} if the package is suspended or {@code false} if the package is not
@@ -5505,6 +5564,103 @@
public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
/**
+ * Query if an app is currently suspended.
+ *
+ * @return {@code true} if the given package is suspended, {@code false} otherwise
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ * @hide
+ */
+ @SystemApi
+ public boolean isPackageSuspended(String packageName) {
+ throw new UnsupportedOperationException("isPackageSuspended not implemented");
+ }
+
+ /**
+ * Apps can query this to know if they have been suspended. A system app with the permission
+ * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state.
+ *
+ * <p>While in this state, the application's notifications will be hidden, any of its started
+ * activities will be stopped and it will not be able to show toasts or dialogs or ring the
+ * device. When the user tries to launch a suspended app, the system will, instead, show a
+ * dialog to the user informing them that they cannot use this app while it is suspended.
+ *
+ * <p>When an app is put into this state, the broadcast action
+ * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast
+ * receivers that included this action in their intent-filters, <em>including manifest
+ * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED}
+ * is delivered when a previously suspended app is taken out of this state.
+ * </p>
+ *
+ * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
+ *
+ * @see #getSuspendedPackageAppExtras()
+ * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+ * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+ */
+ public boolean isPackageSuspended() {
+ throw new UnsupportedOperationException("isPackageSuspended not implemented");
+ }
+
+ /**
+ * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
+ * package was suspended.
+ *
+ * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
+ * api.</p>
+ *
+ * @param packageName The package to retrieve extras for.
+ * @return The {@code appExtras} for the suspended package.
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+ public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+ throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
+ * Set the app extras for a suspended package. This method can be used to update the appExtras
+ * for a package that was earlier suspended using
+ * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
+ * String)}
+ * Does nothing if the given package is not already in a suspended state.
+ *
+ * @param packageName The package for which the appExtras need to be updated
+ * @param appExtras The new appExtras for the given package
+ *
+ * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SUSPEND_APPS)
+ public void setSuspendedPackageAppExtras(String packageName,
+ @Nullable PersistableBundle appExtras) {
+ throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
+ * Returns any extra information supplied as {@code appExtras} to the system when the calling
+ * app was suspended.
+ *
+ * <p>Note: If no extras were supplied to the system, this method will return {@code null}, even
+ * when the calling app has been suspended.</p>
+ *
+ * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
+ * package is not currently suspended.
+ *
+ * @see #isPackageSuspended()
+ * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+ * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+ */
+ public @Nullable Bundle getSuspendedPackageAppExtras() {
+ throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
+ }
+
+ /**
* Provide a hint of what the {@link ApplicationInfo#category} value should
* be for the given package.
* <p>
@@ -5992,4 +6148,26 @@
throw new UnsupportedOperationException(
"hasSigningCertificate not implemented in subclass");
}
+
+ /**
+ * @return the system defined text classifier package name, or null if there's none.
+ *
+ * @hide
+ */
+ public String getSystemTextClassifierPackageName() {
+ throw new UnsupportedOperationException(
+ "getSystemTextClassifierPackageName not implemented in subclass");
+ }
+
+ /**
+ * @return whether a given package's state is protected, e.g. package cannot be disabled,
+ * suspended, hidden or force stopped.
+ *
+ * @hide
+ */
+ public boolean isPackageStateProtected(String packageName, int userId) {
+ throw new UnsupportedOperationException(
+ "isPackageStateProtected not implemented in subclass");
+ }
+
}
diff --git a/android/content/pm/PackageManagerInternal.java b/android/content/pm/PackageManagerInternal.java
index 6f093ba..c9b78c0 100644
--- a/android/content/pm/PackageManagerInternal.java
+++ b/android/content/pm/PackageManagerInternal.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.util.SparseArray;
import java.lang.annotation.Retention;
@@ -43,12 +44,14 @@
public static final int PACKAGE_INSTALLER = 2;
public static final int PACKAGE_VERIFIER = 3;
public static final int PACKAGE_BROWSER = 4;
+ public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
@IntDef(value = {
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
PACKAGE_INSTALLER,
PACKAGE_VERIFIER,
PACKAGE_BROWSER,
+ PACKAGE_SYSTEM_TEXT_CLASSIFIER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KnownPackage {}
@@ -186,6 +189,22 @@
@PackageInfoFlags int flags, int filterCallingUid, int userId);
/**
+ * Retrieve launcher extras for a suspended package provided to the system in
+ * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)}
+ *
+ * @param packageName The package for which to return launcher extras.
+ * @param userId The user for which to check,
+ * @return The launcher extras.
+ *
+ * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+ * PersistableBundle, String)
+ * @see PackageManager#isPackageSuspended()
+ */
+ public abstract Bundle getSuspendedPackageLauncherExtras(String packageName,
+ int userId);
+
+ /**
* Do a straight uid lookup for the given package/application in the given user.
* @see PackageManager#getPackageUidAsUser(String, int, int)
* @return The app's uid, or < 0 if the package was not found in that user
@@ -234,6 +253,11 @@
int userId);
/**
+ * @return The default home activity component name.
+ */
+ public abstract ComponentName getDefaultHomeActivity(int userId);
+
+ /**
* Called by DeviceOwnerManagerService to set the package names of device owner and profile
* owners.
*/
@@ -444,6 +468,9 @@
/** Whether the binder caller can access instant apps. */
public abstract boolean canAccessInstantApps(int callingUid, int userId);
+ /** Whether the binder caller can access the given component. */
+ public abstract boolean canAccessComponent(int callingUid, ComponentName component, int userId);
+
/**
* Returns {@code true} if a given package has instant application meta-data.
* Otherwise, returns {@code false}. Meta-data is state (eg. cookie, app icon, etc)
@@ -537,4 +564,18 @@
/** Updates the flags for the given permission. */
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
@NonNull String packageName, int flagMask, int flagValues, int userId);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSigHash.
+ */
+ public abstract boolean isDataRestoreSafe(@NonNull byte[] restoringFromSigHash,
+ @NonNull String packageName);
+
+ /**
+ * Returns true if it's still safe to restore data backed up from this app's version
+ * that was signed with restoringFromSig.
+ */
+ public abstract boolean isDataRestoreSafe(@NonNull Signature restoringFromSig,
+ @NonNull String packageName);
}
diff --git a/android/content/pm/PackageParser.java b/android/content/pm/PackageParser.java
index 24e3dfa..2f0faf2 100644
--- a/android/content/pm/PackageParser.java
+++ b/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -78,8 +79,10 @@
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Base64;
+import android.util.ByteStringUtils;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.PackageUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -194,10 +197,6 @@
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";
-
private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
/**
@@ -678,6 +677,7 @@
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
pi.overlayTarget = p.mOverlayTarget;
+ pi.overlayCategory = p.mOverlayCategory;
pi.overlayPriority = p.mOverlayPriority;
pi.mOverlayIsStatic = p.mOverlayIsStatic;
pi.compileSdkVersion = p.mCompileSdkVersion;
@@ -1288,7 +1288,6 @@
*/
@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) {
@@ -1297,8 +1296,9 @@
}
}
+ final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
- final Package pkg = parseBaseApk(apkFile, assets, flags);
+ final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
@@ -1306,28 +1306,10 @@
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
- IoUtils.closeQuietly(assets);
+ IoUtils.closeQuietly(assetLoader);
}
}
- 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();
@@ -1343,13 +1325,15 @@
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);
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
@@ -1384,15 +1368,18 @@
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);
+ // This must always succeed, as the path has been added to the AssetManager before.
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
+
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
+ res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
@@ -1496,9 +1483,9 @@
* populating {@link Package#mSigningDetails}. Also asserts that all APK
* contents are signed correctly and consistently.
*/
- public static void collectCertificates(Package pkg, @ParseFlags int parseFlags)
+ public static void collectCertificates(Package pkg, boolean skipVerify)
throws PackageParserException {
- collectCertificatesInternal(pkg, parseFlags);
+ collectCertificatesInternal(pkg, skipVerify);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
Package childPkg = pkg.childPackages.get(i);
@@ -1506,17 +1493,17 @@
}
}
- private static void collectCertificatesInternal(Package pkg, @ParseFlags int parseFlags)
+ private static void collectCertificatesInternal(Package pkg, boolean skipVerify)
throws PackageParserException {
pkg.mSigningDetails = SigningDetails.UNKNOWN;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- collectCertificates(pkg, new File(pkg.baseCodePath), parseFlags);
+ collectCertificates(pkg, new File(pkg.baseCodePath), skipVerify);
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
- collectCertificates(pkg, new File(pkg.splitCodePaths[i]), parseFlags);
+ collectCertificates(pkg, new File(pkg.splitCodePaths[i]), skipVerify);
}
}
} finally {
@@ -1524,7 +1511,7 @@
}
}
- private static void collectCertificates(Package pkg, File apkFile, @ParseFlags int parseFlags)
+ private static void collectCertificates(Package pkg, File apkFile, boolean skipVerify)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
@@ -1534,7 +1521,7 @@
minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
}
SigningDetails verified;
- if ((parseFlags & PARSE_IS_SYSTEM_DIR) != 0) {
+ if (skipVerify) {
// systemDir APKs are already trusted, save time by not verifying
verified = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
apkPath, minSignatureScheme);
@@ -1594,29 +1581,28 @@
int flags) throws PackageParserException {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
- AssetManager assets = null;
XmlResourceParser parser = null;
try {
- assets = newConfiguredAssetManager();
- int cookie = fd != null
- ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath);
- if (cookie == 0) {
+ final ApkAssets apkAssets;
+ try {
+ apkAssets = fd != null
+ ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
+ : ApkAssets.loadFromPath(apkPath);
+ } catch (IOException e) {
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);
+ parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
final Package tempPkg = new Package((String) null);
+ final boolean skipVerify = (flags & PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- collectCertificates(tempPkg, apkFile, flags);
+ collectCertificates(tempPkg, apkFile, skipVerify);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -1634,7 +1620,7 @@
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
- IoUtils.closeQuietly(assets);
+ // TODO(b/72056911): Implement and call close() on ApkAssets.
}
}
@@ -2074,6 +2060,8 @@
com.android.internal.R.styleable.AndroidManifestResourceOverlay);
pkg.mOverlayTarget = sa.getString(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+ pkg.mOverlayCategory = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
pkg.mOverlayPriority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
0);
@@ -3183,12 +3171,12 @@
perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
- if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
+ if (perm.info.getProtectionFlags() != 0) {
if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
&& (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
- outError[0] = "<permission> protectionLevel specifies a non-instnat flag but is "
+ outError[0] = "<permission> protectionLevel specifies a non-instant flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
@@ -3504,7 +3492,7 @@
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_usesCleartextTraffic,
- true)) {
+ owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.P)) {
ai.flags |= ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
}
@@ -3639,7 +3627,9 @@
// getting added to the wrong package.
final CachedComponentArgs cachedArgs = new CachedComponentArgs();
int type;
-
+ boolean hasActivityOrder = false;
+ boolean hasReceiverOrder = false;
+ boolean hasServiceOrder = false;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3655,6 +3645,7 @@
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
@@ -3665,6 +3656,7 @@
return false;
}
+ hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
@@ -3674,6 +3666,7 @@
return false;
}
+ hasServiceOrder |= (s.order != 0);
owner.services.add(s);
} else if (tagName.equals("provider")) {
@@ -3692,6 +3685,7 @@
return false;
}
+ hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
@@ -3825,6 +3819,15 @@
}
}
+ if (hasActivityOrder) {
+ Collections.sort(owner.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
+ }
+ if (hasReceiverOrder) {
+ Collections.sort(owner.receivers, (r1, r2) -> Integer.compare(r2.order, r1.order));
+ }
+ if (hasServiceOrder) {
+ Collections.sort(owner.services, (s1, s2) -> Integer.compare(s2.order, s1.order));
+ }
// Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(owner);
@@ -4366,6 +4369,7 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4427,24 +4431,6 @@
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 {
@@ -4694,6 +4680,7 @@
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
info.maxAspectRatio = target.info.maxAspectRatio;
+ info.requestedVrComponent = target.info.requestedVrComponent;
info.encryptionAware = info.directBootAware = target.info.directBootAware;
@@ -4761,6 +4748,7 @@
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
+ a.order = Math.max(intent.getOrder(), a.order);
a.intents.add(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
@@ -4939,7 +4927,7 @@
p.info.authority = cpname.intern();
if (!parseProviderTags(
- res, parser, visibleToEphemeral, owner, p, outError)) {
+ res, parser, visibleToEphemeral, p, outError)) {
return null;
}
@@ -4947,7 +4935,7 @@
}
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
- boolean visibleToEphemeral, Package owner, Provider outInfo, String[] outError)
+ boolean visibleToEphemeral, Provider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
@@ -4968,6 +4956,7 @@
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ outInfo.order = Math.max(intent.getOrder(), outInfo.order);
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
@@ -4975,17 +4964,6 @@
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,
@@ -5268,23 +5246,13 @@
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
+ s.order = Math.max(intent.getOrder(), s.order);
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 <service>: "
@@ -5504,6 +5472,10 @@
com.android.internal.R.styleable.AndroidManifestIntentFilter_priority, 0);
outInfo.setPriority(priority);
+ int order = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestIntentFilter_order, 0);
+ outInfo.setOrder(order);
+
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestIntentFilter_label);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
@@ -5682,7 +5654,10 @@
return true;
}
- /** A container for signing-related data of an application package. */
+ /**
+ * A container for signing-related data of an application package.
+ * @hide
+ */
public static final class SigningDetails implements Parcelable {
@IntDef({SigningDetails.SignatureSchemeVersion.UNKNOWN,
@@ -5704,15 +5679,58 @@
public final ArraySet<PublicKey> publicKeys;
/**
- * Collection of {@code Signature} objects, each of which is formed from a former signing
- * certificate of this APK before it was changed by signing certificate rotation.
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * This collection of {@code Signature} objects, each of which is formed from a former
+ * signing certificate of this APK before it was changed by signing certificate rotation,
+ * represents the first piece of information. It is the APK saying to the rest of the
+ * world: "hey if you trust the old cert, you can trust me!" This is useful, if for
+ * instance, the platform would like to determine whether or not to allow this APK to do
+ * something it would've allowed it to do under the old cert (like upgrade).
*/
@Nullable
public final Signature[] pastSigningCertificates;
+ /** special value used to see if cert is in package - not exposed to callers */
+ private static final int PAST_CERT_EXISTS = 0;
+
+ @IntDef(
+ flag = true,
+ value = {CertCapabilities.INSTALLED_DATA,
+ CertCapabilities.SHARED_USER_ID,
+ CertCapabilities.PERMISSION,
+ CertCapabilities.ROLLBACK})
+ public @interface CertCapabilities {
+
+ /** accept data from already installed pkg with this cert */
+ int INSTALLED_DATA = 1;
+
+ /** accept sharedUserId with pkg with this cert */
+ int SHARED_USER_ID = 2;
+
+ /** grant SIGNATURE permissions to pkgs with this cert */
+ int PERMISSION = 4;
+
+ /** allow pkg to update to one signed by this certificate */
+ int ROLLBACK = 8;
+ }
+
/**
- * Flags for the {@code pastSigningCertificates} collection, which indicate the capabilities
- * the including APK wishes to grant to its past signing certificates.
+ * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+ * contains two pieces of information:
+ * 1) the past signing certificates
+ * 2) the flags that APK wants to assign to each of the past signing certificates.
+ *
+ * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
+ * collection, represent the second piece of information and are viewed as capabilities.
+ * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+ * please enforce that." This is useful for situation where this app itself is using its
+ * signing certificate as an authorization mechanism, like whether or not to allow another
+ * app to have its SIGNATURE permission. An app could specify whether to allow other apps
+ * signed by its old cert 'X' to still get a signature permission it defines, for example.
*/
@Nullable
public final int[] pastSigningCertificatesFlags;
@@ -5783,6 +5801,244 @@
return pastSigningCertificates != null && pastSigningCertificates.length > 0;
}
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of or the same as this one.
+ * If the {@code oldDetails} signing certificate appears in our pastSigningCertificates,
+ * then that means it has authorized a signing certificate rotation, which eventually leads
+ * to our certificate, and thus can be trusted. If this method evaluates to true, this
+ * SigningDetails object should be trusted if the previous one is.
+ */
+ public boolean hasAncestorOrSelf(SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.signatures.length > 1) {
+
+ // multiple-signer packages cannot rotate signing certs, so we just compare current
+ // signers for an exact match
+ return signaturesMatchExactly(oldDetails);
+ } else {
+
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates
+ return hasCertificate(oldDetails.signatures[0]);
+ }
+ }
+
+ /**
+ * Similar to {@code hasAncestorOrSelf}. Returns true only if this {@code SigningDetails}
+ * is a descendant of {@code oldDetails}, not if they're the same. This is used to
+ * determine if this object is newer than the provided one.
+ */
+ public boolean hasAncestor(SigningDetails oldDetails) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (this.hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+ // the last entry in pastSigningCertificates is the current signer, ignore it
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ if (pastSigningCertificates[i].equals(oldDetails.signatures[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
+ * not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ */
+ public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) {
+ if (this == UNKNOWN || oldDetails == UNKNOWN) {
+ return false;
+ }
+ if (oldDetails.signatures.length > 1) {
+
+ // multiple-signer packages cannot rotate signing certs, so we must have an exact
+ // match, which also means all capabilities are granted
+ return signaturesMatchExactly(oldDetails);
+ } else {
+
+ // we may have signing certificate rotation history, check to see if the oldDetails
+ // was one of our old signing certificates, and if we grant it the capability it's
+ // requesting
+ return hasCertificate(oldDetails.signatures[0], flags);
+ }
+ }
+
+ /**
+ * A special case of {@code checkCapability} which re-encodes both sets of signing
+ * certificates to counteract a previous re-encoding.
+ */
+ public boolean checkCapabilityRecover(SigningDetails oldDetails,
+ @CertCapabilities int flags) throws CertificateException {
+ if (oldDetails == UNKNOWN || this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates() && oldDetails.signatures.length == 1) {
+
+ // signing certificates may have rotated, check entire history for effective match
+ for (int i = 0; i < pastSigningCertificates.length; i++) {
+ if (Signature.areEffectiveMatch(
+ oldDetails.signatures[0],
+ pastSigningCertificates[i])
+ && pastSigningCertificatesFlags[i] == flags) {
+ return true;
+ }
+ }
+ } else {
+ return Signature.areEffectiveMatch(oldDetails.signatures, signatures);
+ }
+ return false;
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer. Automatically returns false if this object has multiple
+ * signing certificates, since rotation is only supported for single-signers; this is
+ * enforced by {@code hasCertificateInternal}.
+ */
+ public boolean hasCertificate(Signature signature) {
+ return hasCertificateInternal(signature, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if {@code signature} is in this SigningDetails' signing certificate history,
+ * including the current signer, and whether or not it has the given permission.
+ * Certificates which match our current signer automatically get all capabilities.
+ * Automatically returns false if this object has multiple signing certificates, since
+ * rotation is only supported for single-signers.
+ */
+ public boolean hasCertificate(Signature signature, @CertCapabilities int flags) {
+ return hasCertificateInternal(signature, flags);
+ }
+
+ /** Convenient wrapper for calling {@code hasCertificate} with certificate's raw bytes. */
+ public boolean hasCertificate(byte[] certificate) {
+ Signature signature = new Signature(certificate);
+ return hasCertificate(signature);
+ }
+
+ private boolean hasCertificateInternal(Signature signature, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // only single-signed apps can have pastSigningCertificates
+ if (hasPastSigningCertificates()) {
+
+ // check all past certs, except for the current one, which automatically gets all
+ // capabilities, since it is the same as the current signature
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ if (pastSigningCertificates[i].equals(signature)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer and make sure
+ // we are singly-signed
+ return signatures.length == 1 && signatures[0].equals(signature);
+ }
+
+ /**
+ * Determines if the provided {@code sha256String} is an ancestor of this one, and whether
+ * or not this one grants it the provided capability, represented by the {@code flags}
+ * parameter. In the event of signing certificate rotation, a package may still interact
+ * with entities signed by its old signing certificate and not want to break previously
+ * functioning behavior. The {@code flags} value determines which capabilities the app
+ * signed by the newer signing certificate would like to continue to give to its previous
+ * signing certificate(s).
+ *
+ * @param sha256String A hex-encoded representation of a sha256 digest. In the case of an
+ * app with multiple signers, this represents the hex-encoded sha256
+ * digest of the combined hex-encoded sha256 digests of each individual
+ * signing certificate according to {@link
+ * PackageUtils#computeSignaturesSha256Digest(Signature[])}
+ */
+ public boolean checkCapability(String sha256String, @CertCapabilities int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+
+ // first see if the hash represents a single-signer in our signing history
+ byte[] sha256Bytes = ByteStringUtils.fromHexToByteArray(sha256String);
+ if (hasSha256Certificate(sha256Bytes, flags)) {
+ return true;
+ }
+
+ // Not in signing history, either represents multiple signatures or not a match.
+ // Multiple signers can't rotate, so no need to check flags, just see if the SHAs match.
+ // We already check the single-signer case above as part of hasSha256Certificate, so no
+ // need to verify we have multiple signers, just run the old check
+ // just consider current signing certs
+ final String[] mSignaturesSha256Digests =
+ PackageUtils.computeSignaturesSha256Digests(signatures);
+ final String mSignaturesSha256Digest =
+ PackageUtils.computeSignaturesSha256Digest(mSignaturesSha256Digests);
+ return mSignaturesSha256Digest.equals(sha256String);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} is in this SigningDetails' signing certificate
+ * history, including the current signer. Automatically returns false if this object has
+ * multiple signing certificates, since rotation is only supported for single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate) {
+ return hasSha256CertificateInternal(sha256Certificate, PAST_CERT_EXISTS);
+ }
+
+ /**
+ * Determine if the {@code sha256Certificate} certificate hash corresponds to a signing
+ * certificate in this SigningDetails' signing certificate history, including the current
+ * signer, and whether or not it has the given permission. Certificates which match our
+ * current signer automatically get all capabilities. Automatically returns false if this
+ * object has multiple signing certificates, since rotation is only supported for
+ * single-signers.
+ */
+ public boolean hasSha256Certificate(byte[] sha256Certificate, @CertCapabilities int flags) {
+ return hasSha256CertificateInternal(sha256Certificate, flags);
+ }
+
+ private boolean hasSha256CertificateInternal(byte[] sha256Certificate, int flags) {
+ if (this == UNKNOWN) {
+ return false;
+ }
+ if (hasPastSigningCertificates()) {
+
+ // check all past certs, except for the last one, which automatically gets all
+ // capabilities, since it is the same as the current signature, and is checked below
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ byte[] digest = PackageUtils.computeSha256DigestBytes(
+ pastSigningCertificates[i].toByteArray());
+ if (Arrays.equals(sha256Certificate, digest)) {
+ if (flags == PAST_CERT_EXISTS
+ || (flags & pastSigningCertificatesFlags[i]) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+
+ // not in previous certs signing history, just check the current signer
+ if (signatures.length == 1) {
+ byte[] digest =
+ PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+ return Arrays.equals(sha256Certificate, digest);
+ }
+ return false;
+ }
+
/** Returns true if the signatures in this and other match exactly. */
public boolean signaturesMatchExactly(SigningDetails other) {
return Signature.areExactMatch(this.signatures, other.signatures);
@@ -6085,6 +6341,7 @@
public String mRequiredAccountType;
public String mOverlayTarget;
+ public String mOverlayCategory;
public int mOverlayPriority;
public boolean mOverlayIsStatic;
@@ -6393,6 +6650,11 @@
}
/** @hide */
+ public boolean isProduct() {
+ return applicationInfo.isProduct();
+ }
+
+ /** @hide */
public boolean isPrivileged() {
return applicationInfo.isPrivilegedApp();
}
@@ -6450,31 +6712,6 @@
+ " " + packageName + "}";
}
- public String dumpState_temp() {
- String flags = "";
- flags += ((applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ? "U" : "");
- flags += ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? "S" : "");
- if ("".equals(flags)) {
- flags = "-";
- }
- String privFlags = "";
- privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0 ? "P" : "");
- privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0 ? "O" : "");
- privFlags += ((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0 ? "V" : "");
- if ("".equals(privFlags)) {
- privFlags = "-";
- }
- return "Package{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + packageName
- + ", ver:" + getLongVersionCode()
- + ", path: " + codePath
- + ", flags: " + flags
- + ", privFlags: " + privFlags
- + ", extra: " + (mExtras == null ? "<<NULL>>" : Integer.toHexString(System.identityHashCode(mExtras)) + "}")
- + "}";
- }
-
@Override
public int describeContents() {
return 0;
@@ -6615,6 +6852,7 @@
mRestrictedAccountType = dest.readString();
mRequiredAccountType = dest.readString();
mOverlayTarget = dest.readString();
+ mOverlayCategory = dest.readString();
mOverlayPriority = dest.readInt();
mOverlayIsStatic = (dest.readInt() == 1);
mCompileSdkVersion = dest.readInt();
@@ -6738,6 +6976,7 @@
dest.writeString(mRestrictedAccountType);
dest.writeString(mRequiredAccountType);
dest.writeString(mOverlayTarget);
+ dest.writeString(mOverlayCategory);
dest.writeInt(mOverlayPriority);
dest.writeInt(mOverlayIsStatic ? 1 : 0);
dest.writeInt(mCompileSdkVersion);
@@ -6828,6 +7067,8 @@
public Bundle metaData;
public Package owner;
+ /** The order of this component in relation to its peers */
+ public int order;
ComponentName componentName;
String componentShortName;
@@ -7346,6 +7587,7 @@
for (ActivityIntentInfo aii : intents) {
aii.activity = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
@@ -7435,6 +7677,7 @@
for (ServiceIntentInfo aii : intents) {
aii.service = this;
+ order = Math.max(aii.getOrder(), order);
}
if (info.permission != null) {
diff --git a/android/content/pm/PackageSharedLibraryUpdater.java b/android/content/pm/PackageSharedLibraryUpdater.java
index 49d884c..fa89432 100644
--- a/android/content/pm/PackageSharedLibraryUpdater.java
+++ b/android/content/pm/PackageSharedLibraryUpdater.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -38,6 +39,12 @@
*/
public abstract void updatePackage(PackageParser.Package pkg);
+ static void removeLibrary(PackageParser.Package pkg, String libraryName) {
+ pkg.usesLibraries = ArrayUtils.remove(pkg.usesLibraries, libraryName);
+ pkg.usesOptionalLibraries =
+ ArrayUtils.remove(pkg.usesOptionalLibraries, libraryName);
+ }
+
static @NonNull
<T> ArrayList<T> prefix(@Nullable ArrayList<T> cur, T val) {
if (cur == null) {
@@ -47,9 +54,54 @@
return cur;
}
- static boolean isLibraryPresent(ArrayList<String> usesLibraries,
+ private static boolean isLibraryPresent(ArrayList<String> usesLibraries,
ArrayList<String> usesOptionalLibraries, String apacheHttpLegacy) {
return ArrayUtils.contains(usesLibraries, apacheHttpLegacy)
|| ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy);
}
+
+ static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) {
+ int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
+ return targetSdkVersion <= Build.VERSION_CODES.O_MR1;
+ }
+
+ /**
+ * Add an implicit dependency.
+ *
+ * <p>If the package has an existing dependency on {@code existingLibrary} then prefix it with
+ * the {@code implicitDependency} if it is not already in the list of libraries.
+ *
+ * @param pkg the {@link PackageParser.Package} to update.
+ * @param existingLibrary the existing library.
+ * @param implicitDependency the implicit dependency to add
+ */
+ void prefixImplicitDependency(PackageParser.Package pkg, String existingLibrary,
+ String implicitDependency) {
+ ArrayList<String> usesLibraries = pkg.usesLibraries;
+ ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+ if (!isLibraryPresent(usesLibraries, usesOptionalLibraries, implicitDependency)) {
+ if (ArrayUtils.contains(usesLibraries, existingLibrary)) {
+ prefix(usesLibraries, implicitDependency);
+ } else if (ArrayUtils.contains(usesOptionalLibraries, existingLibrary)) {
+ prefix(usesOptionalLibraries, implicitDependency);
+ }
+
+ pkg.usesLibraries = usesLibraries;
+ pkg.usesOptionalLibraries = usesOptionalLibraries;
+ }
+ }
+
+ void prefixRequiredLibrary(PackageParser.Package pkg, String libraryName) {
+ ArrayList<String> usesLibraries = pkg.usesLibraries;
+ ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+ boolean alreadyPresent = isLibraryPresent(
+ usesLibraries, usesOptionalLibraries, libraryName);
+ if (!alreadyPresent) {
+ usesLibraries = prefix(usesLibraries, libraryName);
+
+ pkg.usesLibraries = usesLibraries;
+ }
+ }
}
diff --git a/android/content/pm/PackageUserState.java b/android/content/pm/PackageUserState.java
index 293beb2..f7b6e09 100644
--- a/android/content/pm/PackageUserState.java
+++ b/android/content/pm/PackageUserState.java
@@ -27,6 +27,8 @@
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+import android.os.BaseBundle;
+import android.os.PersistableBundle;
import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
@@ -44,6 +46,9 @@
public boolean notLaunched;
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
+ public String suspendingPackage;
+ public PersistableBundle suspendedAppExtras;
+ public PersistableBundle suspendedLauncherExtras;
public boolean instantApp;
public boolean virtualPreload;
public int enabled;
@@ -76,6 +81,9 @@
notLaunched = o.notLaunched;
hidden = o.hidden;
suspended = o.suspended;
+ suspendingPackage = o.suspendingPackage;
+ suspendedAppExtras = o.suspendedAppExtras;
+ suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp;
virtualPreload = o.virtualPreload;
enabled = o.enabled;
@@ -195,6 +203,20 @@
if (suspended != oldState.suspended) {
return false;
}
+ if (suspended) {
+ if (suspendingPackage == null
+ || !suspendingPackage.equals(oldState.suspendingPackage)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(suspendedAppExtras,
+ oldState.suspendedAppExtras)) {
+ return false;
+ }
+ if (!BaseBundle.kindofEquals(suspendedLauncherExtras,
+ oldState.suspendedLauncherExtras)) {
+ return false;
+ }
+ }
if (instantApp != oldState.instantApp) {
return false;
}
diff --git a/android/content/pm/PermissionInfo.java b/android/content/pm/PermissionInfo.java
index 21bd7f0..938409a 100644
--- a/android/content/pm/PermissionInfo.java
+++ b/android/content/pm/PermissionInfo.java
@@ -16,12 +16,16 @@
package android.content.pm;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Information you can retrieve about a particular security permission
* known to the system. This corresponds to information collected from the
@@ -56,6 +60,16 @@
@Deprecated
public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
+ /** @hide */
+ @IntDef(flag = false, prefix = { "PROTECTION_" }, value = {
+ PROTECTION_NORMAL,
+ PROTECTION_DANGEROUS,
+ PROTECTION_SIGNATURE,
+ PROTECTION_SIGNATURE_OR_SYSTEM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Protection {}
+
/**
* Additional flag for {@link #protectionLevel}, corresponding
* to the <code>privileged</code> value of
@@ -155,30 +169,71 @@
public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
/**
- * Mask for {@link #protectionLevel}: the basic protection type.
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>text_classifier</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
*/
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
+ PROTECTION_FLAG_PRIVILEGED,
+ PROTECTION_FLAG_SYSTEM,
+ PROTECTION_FLAG_DEVELOPMENT,
+ PROTECTION_FLAG_APPOP,
+ PROTECTION_FLAG_PRE23,
+ PROTECTION_FLAG_INSTALLER,
+ PROTECTION_FLAG_VERIFIER,
+ PROTECTION_FLAG_PREINSTALLED,
+ PROTECTION_FLAG_SETUP,
+ PROTECTION_FLAG_INSTANT,
+ PROTECTION_FLAG_RUNTIME_ONLY,
+ PROTECTION_FLAG_OEM,
+ PROTECTION_FLAG_VENDOR_PRIVILEGED,
+ PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProtectionFlags {}
+
+ /**
+ * Mask for {@link #protectionLevel}: the basic protection type.
+ *
+ * @deprecated Use #getProtection() instead.
+ */
+ @Deprecated
public static final int PROTECTION_MASK_BASE = 0xf;
/**
* Mask for {@link #protectionLevel}: additional flag bits.
+ *
+ * @deprecated Use #getProtectionFlags() instead.
*/
+ @Deprecated
public static final int PROTECTION_MASK_FLAGS = 0xfff0;
/**
* The level of access this permission is protecting, as per
* {@link android.R.attr#protectionLevel}. Consists of
- * a base permission type and zero or more flags:
+ * a base permission type and zero or more flags. Use the following functions
+ * to extract them.
*
* <pre>
- * int basePermissionType = protectionLevel & {@link #PROTECTION_MASK_BASE};
- * int permissionFlags = protectionLevel & {@link #PROTECTION_MASK_FLAGS};
+ * int basePermissionType = permissionInfo.getProtection();
+ * int permissionFlags = permissionInfo.getProtectionFlags();
* </pre>
*
* <p></p>Base permission types are {@link #PROTECTION_NORMAL},
* {@link #PROTECTION_DANGEROUS}, {@link #PROTECTION_SIGNATURE}
* and the deprecated {@link #PROTECTION_SIGNATURE_OR_SYSTEM}.
* Flags are listed under {@link android.R.attr#protectionLevel}.
+ *
+ * @deprecated Use #getProtection() and #getProtectionFlags() instead.
*/
+ @Deprecated
public int protectionLevel;
/**
@@ -304,6 +359,9 @@
if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
protLevel += "|vendorPrivileged";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
+ protLevel += "|textClassifier";
+ }
return protLevel;
}
@@ -344,6 +402,22 @@
return null;
}
+ /**
+ * Return the base permission type.
+ */
+ @Protection
+ public int getProtection() {
+ return protectionLevel & PROTECTION_MASK_BASE;
+ }
+
+ /**
+ * Return the additional flags in {@link #protectionLevel}.
+ */
+ @ProtectionFlags
+ public int getProtectionFlags() {
+ return protectionLevel & ~PROTECTION_MASK_BASE;
+ }
+
@Override
public String toString() {
return "PermissionInfo{"
diff --git a/android/content/pm/ResolveInfo.java b/android/content/pm/ResolveInfo.java
index 3f63d80..fc2eba2 100644
--- a/android/content/pm/ResolveInfo.java
+++ b/android/content/pm/ResolveInfo.java
@@ -277,7 +277,7 @@
dr = pm.getDrawable(ci.packageName, iconResourceId, ai);
}
if (dr != null) {
- return pm.getUserBadgedIcon(dr, new UserHandle(UserHandle.myUserId()));
+ return pm.getUserBadgedIcon(dr, new UserHandle(pm.getUserId()));
}
return ci.loadIcon(pm);
}
diff --git a/android/content/pm/SharedLibraryNames.java b/android/content/pm/SharedLibraryNames.java
new file mode 100644
index 0000000..83e8663
--- /dev/null
+++ b/android/content/pm/SharedLibraryNames.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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;
+
+/**
+ * A set of shared library names
+ *
+ * @hide
+ */
+public class SharedLibraryNames {
+
+ static final String ANDROID_TEST_BASE = "android.test.base";
+
+ static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+ static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+ static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+}
diff --git a/android/content/pm/ShortcutManager.java b/android/content/pm/ShortcutManager.java
index 30222b7..25e0ccd 100644
--- a/android/content/pm/ShortcutManager.java
+++ b/android/content/pm/ShortcutManager.java
@@ -770,6 +770,6 @@
/** @hide injection point */
@VisibleForTesting
protected int injectMyUserId() {
- return UserHandle.myUserId();
+ return mContext.getUserId();
}
}
diff --git a/android/content/pm/Signature.java b/android/content/pm/Signature.java
index fdc54ae..a2a14ed 100644
--- a/android/content/pm/Signature.java
+++ b/android/content/pm/Signature.java
@@ -285,6 +285,29 @@
}
/**
+ * Test if given {@link Signature} objects are effectively equal. In rare
+ * cases, certificates can have slightly malformed encoding which causes
+ * exact-byte checks to fail.
+ * <p>
+ * To identify effective equality, we bounce the certificates through an
+ * decode/encode pass before doing the exact-byte check. To reduce attack
+ * surface area, we only allow a byte size delta of a few bytes.
+ *
+ * @throws CertificateException if the before/after length differs
+ * substantially, usually a signal of something fishy going on.
+ * @hide
+ */
+ public static boolean areEffectiveMatch(Signature a, Signature b)
+ throws CertificateException {
+ final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ final Signature aPrime = bounce(cf, a);
+ final Signature bPrime = bounce(cf, b);
+
+ return aPrime.equals(bPrime);
+ }
+
+ /**
* Bounce the given {@link Signature} through a decode/encode cycle.
*
* @throws CertificateException if the before/after length differs
diff --git a/android/content/pm/VerifierDeviceIdentity.java b/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6a..90be6f3 100644
--- a/android/content/pm/VerifierDeviceIdentity.java
+++ b/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Random;
@@ -86,6 +88,7 @@
* @return verifier device identity based on the input from the provided
* random number generator
*/
+ @VisibleForTesting
static VerifierDeviceIdentity generate(Random rng) {
long identity = rng.nextLong();
return new VerifierDeviceIdentity(identity);
diff --git a/android/content/pm/dex/ArtManager.java b/android/content/pm/dex/ArtManager.java
index aa9c46e..4129398 100644
--- a/android/content/pm/dex/ArtManager.java
+++ b/android/content/pm/dex/ArtManager.java
@@ -16,16 +16,22 @@
package android.content.pm.dex;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Slog;
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
/**
* Class for retrieving various kinds of information related to the runtime artifacts of
* packages that are currently installed on the device.
@@ -43,6 +49,20 @@
/** The snapshot failed because of an internal error (e.g. error during opening profiles). */
public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
+ /** Constant used for applications profiles. */
+ public static final int PROFILE_APPS = 0;
+ /** Constant used for the boot image profile. */
+ public static final int PROFILE_BOOT_IMAGE = 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PROFILE_" }, value = {
+ PROFILE_APPS,
+ PROFILE_BOOT_IMAGE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ProfileType {}
+
+
private IArtManager mArtManager;
/**
@@ -53,41 +73,59 @@
}
/**
- * Snapshots the runtime profile for an apk belonging to the package {@code packageName}.
- * The apk is identified by {@code codePath}. The calling process must have
- * {@code android.permission.READ_RUNTIME_PROFILE} permission.
+ * Snapshots a runtime profile according to the {@code profileType} parameter.
*
- * The result will be posted on {@code handler} using the given {@code callback}.
- * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}.
+ * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot
+ * the profile for for an apk belonging to the package {@code packageName}.
+ * The apk is identified by {@code codePath}.
*
- * @param packageName the target package name
- * @param codePath the code path for which the profile should be retrieved
+ * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot
+ * the profile for the boot image. In this case {@code codePath can be null}. The parameters
+ * {@code packageName} and {@code codePath} are ignored.
+ *u
+ * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+ *
+ * The result will be posted on the {@code executor} using the given {@code callback}.
+ * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
+ *
+ * This method will throw {@link IllegalStateException} if
+ * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
+ * {@code profileType}.
+ *
+ * @param profileType the type of profile that should be snapshot (boot image or app)
+ * @param packageName the target package name or null if the target is the boot image
+ * @param codePath the code path for which the profile should be retrieved or null if
+ * the target is the boot image
* @param callback the callback which should be used for the result
- * @param handler the handler which should be used to post the result
+ * @param executor the executor which should be used to post the result
*/
@RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
- public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath,
- @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) {
+ public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
+ @Nullable String codePath, @NonNull @CallbackExecutor Executor executor,
+ @NonNull SnapshotRuntimeProfileCallback callback) {
Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
SnapshotRuntimeProfileCallbackDelegate delegate =
- new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper());
+ new SnapshotRuntimeProfileCallbackDelegate(callback, executor);
try {
- mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate);
+ mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
}
/**
- * Returns true if runtime profiles are enabled, false otherwise.
+ * Returns true if runtime profiles are enabled for the given type, false otherwise.
*
* The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+ *
+ * @param profileType can be either {@link ArtManager#PROFILE_APPS}
+ * or {@link ArtManager#PROFILE_BOOT_IMAGE}
*/
@RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
- public boolean isRuntimeProfilingEnabled() {
+ public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) {
try {
- return mArtManager.isRuntimeProfilingEnabled();
+ return mArtManager.isRuntimeProfilingEnabled(profileType);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
@@ -116,41 +154,24 @@
}
private static class SnapshotRuntimeProfileCallbackDelegate
- extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub
- implements Handler.Callback {
- private static final int MSG_SNAPSHOT_OK = 1;
- private static final int MSG_ERROR = 2;
+ extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub {
private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
- private final Handler mHandler;
+ private final Executor mExecutor;
private SnapshotRuntimeProfileCallbackDelegate(
- ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) {
+ ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) {
mCallback = callback;
- mHandler = new Handler(looper, this);
+ mExecutor = executor;
}
@Override
- public void onSuccess(ParcelFileDescriptor profileReadFd) {
- mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget();
+ public void onSuccess(final ParcelFileDescriptor profileReadFd) {
+ mExecutor.execute(() -> mCallback.onSuccess(profileReadFd));
}
@Override
public void onError(int errCode) {
- mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget();
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SNAPSHOT_OK:
- mCallback.onSuccess((ParcelFileDescriptor) msg.obj);
- break;
- case MSG_ERROR:
- mCallback.onError(msg.arg1);
- break;
- default: return false;
- }
- return true;
+ mExecutor.execute(() -> mCallback.onError(errCode));
}
}
@@ -163,4 +184,27 @@
public static String getProfileName(String splitName) {
return splitName == null ? "primary.prof" : splitName + ".split.prof";
}
+
+ /**
+ * Return the path to the current profile corresponding to given package and split.
+ *
+ * @hide
+ */
+ public static String getCurrentProfilePath(String packageName, int userId, String splitName) {
+ File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName);
+ return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
+ }
+
+ /**
+ * Return the snapshot profile file for the given package and profile name.
+ *
+ * KEEP in sync with installd dexopt.cpp.
+ * TODO(calin): inject the snapshot profile name from PM to avoid the dependency.
+ *
+ * @hide
+ */
+ public static File getProfileSnapshotFileForName(String packageName, String profileName) {
+ File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
+ return new File(profileDir, profileName + ".snapshot");
+ }
}
diff --git a/android/content/pm/dex/ArtManagerInternal.java b/android/content/pm/dex/ArtManagerInternal.java
new file mode 100644
index 0000000..62ab9e0
--- /dev/null
+++ b/android/content/pm/dex/ArtManagerInternal.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright 2018 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.dex;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Art manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ArtManagerInternal {
+
+ /**
+ * Return optimization information about the application {@code info} when
+ * in executes using the specified {@code abi}.
+ */
+ public abstract PackageOptimizationInfo getPackageOptimizationInfo(
+ ApplicationInfo info, String abi);
+}
diff --git a/android/content/pm/dex/PackageOptimizationInfo.java b/android/content/pm/dex/PackageOptimizationInfo.java
new file mode 100644
index 0000000..7e7d29e
--- /dev/null
+++ b/android/content/pm/dex/PackageOptimizationInfo.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2018 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.dex;
+
+/**
+ * Encapsulates information about the optimizations performed on a package.
+ *
+ * @hide
+ */
+public class PackageOptimizationInfo {
+ private final int mCompilationFilter;
+ private final int mCompilationReason;
+
+ public PackageOptimizationInfo(int compilerFilter, int compilationReason) {
+ this.mCompilationReason = compilationReason;
+ this.mCompilationFilter = compilerFilter;
+ }
+
+ public int getCompilationReason() {
+ return mCompilationReason;
+ }
+
+ public int getCompilationFilter() {
+ return mCompilationFilter;
+ }
+
+ /**
+ * Create a default optimization info object for the case when we have no information.
+ */
+ public static PackageOptimizationInfo createWithNoInfo() {
+ return new PackageOptimizationInfo(-1, -1);
+ }
+}
diff --git a/android/content/pm/permission/RuntimePermissionPresenter.java b/android/content/pm/permission/RuntimePermissionPresenter.java
index 02d0a6d..79bc9a3 100644
--- a/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -274,6 +274,7 @@
}
}
+ @GuardedBy("mLock")
private void scheduleNextMessageIfNeededLocked() {
if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
Message nextMessage = mPendingWork.remove(0);
diff --git a/android/content/pm/split/DefaultSplitAssetLoader.java b/android/content/pm/split/DefaultSplitAssetLoader.java
index 99eb470..9e3a8f4 100644
--- a/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,10 +15,13 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
@@ -26,6 +29,8 @@
import libcore.io.IoUtils;
+import java.io.IOException;
+
/**
* Loads the base and split APKs into a single AssetManager.
* @hide
@@ -33,68 +38,66 @@
public class DefaultSplitAssetLoader implements SplitAssetLoader {
private final String mBaseCodePath;
private final String[] mSplitCodePaths;
- private final int mFlags;
-
+ private final @ParseFlags int mFlags;
private AssetManager mCachedAssetManager;
- public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) {
+ public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
mBaseCodePath = pkg.baseCodePath;
mSplitCodePaths = pkg.splitCodePaths;
mFlags = flags;
}
- private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
- throws PackageParser.PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) {
- throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + apkPath);
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
}
- if (assets.addAssetPath(apkPath) == 0) {
- throw new PackageParser.PackageParserException(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
}
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParserException {
if (mCachedAssetManager != null) {
return mCachedAssetManager;
}
- AssetManager assets = new AssetManager();
- try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
+ ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+ ? mSplitCodePaths.length : 0) + 1];
- if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
- for (String apkPath : mSplitCodePaths) {
- loadApkIntoAssetManager(assets, apkPath, mFlags);
- }
- }
+ // Load the base.
+ int splitIdx = 0;
+ apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
- mCachedAssetManager = assets;
- assets = null;
- return mCachedAssetManager;
- } finally {
- if (assets != null) {
- IoUtils.closeQuietly(assets);
+ // Load any splits.
+ if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+ for (String apkPath : mSplitCodePaths) {
+ apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
}
}
+
+ AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+ mCachedAssetManager = assets;
+ return mCachedAssetManager;
}
@Override
- public AssetManager getSplitAssetManager(int splitIdx)
- throws PackageParser.PackageParserException {
+ public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
return getBaseAssetManager();
}
@Override
public void close() throws Exception {
- if (mCachedAssetManager != null) {
- IoUtils.closeQuietly(mCachedAssetManager);
- }
+ IoUtils.closeQuietly(mCachedAssetManager);
}
}
diff --git a/android/content/pm/split/SplitAssetDependencyLoader.java b/android/content/pm/split/SplitAssetDependencyLoader.java
index 16023f0..58eaabf 100644
--- a/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,17 +15,21 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.annotation.NonNull;
+import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.ParseFlags;
+import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import android.util.SparseArray;
import libcore.io.IoUtils;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,17 +38,15 @@
* is to be used when an application opts-in to isolated split loading.
* @hide
*/
-public class SplitAssetDependencyLoader
- extends SplitDependencyLoader<PackageParser.PackageParserException>
+public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
implements SplitAssetLoader {
private final String[] mSplitPaths;
- private final int mFlags;
-
- private String[][] mCachedPaths;
- private AssetManager[] mCachedAssetManagers;
+ private final @ParseFlags int mFlags;
+ private final ApkAssets[][] mCachedSplitApks;
+ private final AssetManager[] mCachedAssetManagers;
public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
- SparseArray<int[]> dependencies, int flags) {
+ SparseArray<int[]> dependencies, @ParseFlags int flags) {
super(dependencies);
// The base is inserted into index 0, so we need to shift all the splits by 1.
@@ -53,7 +55,7 @@
System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
mFlags = flags;
- mCachedPaths = new String[mSplitPaths.length][];
+ mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
mCachedAssetManagers = new AssetManager[mSplitPaths.length];
}
@@ -62,58 +64,60 @@
return mCachedAssetManagers[splitIdx] != null;
}
- private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
- throws PackageParser.PackageParserException {
- final AssetManager assets = new AssetManager();
- try {
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
-
- for (String assetPath : assetPaths) {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
- !PackageParser.isApkPath(assetPath)) {
- throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + assetPath);
- }
-
- if (assets.addAssetPath(assetPath) == 0) {
- throw new PackageParser.PackageParserException(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + assetPath);
- }
- }
- return assets;
- } catch (Throwable e) {
- IoUtils.closeQuietly(assets);
- throw e;
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
}
+
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
+ }
+ }
+
+ private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+ final AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+ return assets;
}
@Override
protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
- int parentSplitIdx) throws PackageParser.PackageParserException {
- final ArrayList<String> assetPaths = new ArrayList<>();
+ int parentSplitIdx) throws PackageParserException {
+ final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+ // Include parent ApkAssets.
if (parentSplitIdx >= 0) {
- Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
+ Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
}
- assetPaths.add(mSplitPaths[splitIdx]);
+ // Include this ApkAssets.
+ assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+ // Load and include all config splits for this feature.
for (int configSplitIdx : configSplitIndices) {
- assetPaths.add(mSplitPaths[configSplitIdx]);
+ assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
}
- mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
- mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
- mFlags);
+
+ // Cache the results.
+ mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+ mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParserException {
loadDependenciesForSplit(0);
return mCachedAssetManagers[0];
}
@Override
- public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
+ public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
// Since we insert the base at position 0, and PackageParser keeps splits separate from
// the base, we need to adjust the index.
loadDependenciesForSplit(idx + 1);