Merge "Add native Thermal Throttling API to libandroid."
diff --git a/Android.bp b/Android.bp
index df71601..4bc2e9d2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -327,7 +327,6 @@
"rs/java",
"sax/java",
"telecomm/java",
- "wifi/aidl-export",
],
},
}
@@ -393,7 +392,7 @@
exclude_srcs: [
// See comment on framework-atb-backward-compatibility module below
- "core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java",
+ "core/java/android/content/pm/AndroidTestBaseUpdater.java",
],
sdk_version: "core_platform",
@@ -609,7 +608,7 @@
installable: true,
libs: ["app-compat-annotations"],
srcs: [
- "core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java",
+ "core/java/android/content/pm/AndroidTestBaseUpdater.java",
],
}
@@ -1042,22 +1041,6 @@
},
}
-
-subdirs = [
- "cmds/*",
- "core/*",
- "libs/*",
- "media/*",
- "proto",
- "tools/*",
- "native/android",
- "native/graphics/jni",
-]
-
-optional_subdirs = [
- "core/tests/utiltests/jni",
-]
-
// TODO(b/77285514): remove this once the last few hidl interfaces have been
// updated to use hwbinder.stubs.
java_library {
@@ -1117,13 +1100,6 @@
}
filegroup {
- name: "framework-annotation-nonnull-srcs",
- srcs: [
- "core/java/android/annotation/NonNull.java",
- ],
-}
-
-filegroup {
name: "framework-media-annotation-srcs",
srcs: [
":framework-annotations",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 4616ced..7f23df7 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -49,7 +49,6 @@
":opt-net-voip-srcs",
":core-current-stubs-source",
":core_public_api_files",
- ":ike-api-srcs",
],
// TODO(b/147699819): remove below aidl includes.
aidl: {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 1cc1c5f..18b1108 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -96,10 +96,12 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.Random;
import java.util.Set;
/**
@@ -122,8 +124,15 @@
// Contains all ids that are currently in use.
@GuardedBy("mBlobsLock")
+ private final ArraySet<Long> mActiveBlobIds = new ArraySet<>();
+ // Contains all ids that are currently in use and those that were in use but got deleted in the
+ // current boot session.
+ @GuardedBy("mBlobsLock")
private final ArraySet<Long> mKnownBlobIds = new ArraySet<>();
+ // Random number generator for new session ids.
+ private final Random mRandom = new SecureRandom();
+
private final Context mContext;
private final Handler mHandler;
private final Injector mInjector;
@@ -181,7 +190,16 @@
@GuardedBy("mBlobsLock")
private long generateNextSessionIdLocked() {
- return ++mCurrentMaxSessionId;
+ // Logic borrowed from PackageInstallerService.
+ int n = 0;
+ long sessionId;
+ do {
+ sessionId = Math.abs(mRandom.nextLong());
+ if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != 0) {
+ return sessionId;
+ }
+ } while (n++ < 32);
+ throw new IllegalStateException("Failed to allocate session ID");
}
private void registerReceivers() {
@@ -228,15 +246,22 @@
}
@VisibleForTesting
- void addKnownIdsForTest(long... knownIds) {
+ void addActiveIdsForTest(long... activeIds) {
synchronized (mBlobsLock) {
- for (long id : knownIds) {
- mKnownBlobIds.add(id);
+ for (long id : activeIds) {
+ addActiveBlobIdLocked(id);
}
}
}
@VisibleForTesting
+ Set<Long> getActiveIdsForTest() {
+ synchronized (mBlobsLock) {
+ return mActiveBlobIds;
+ }
+ }
+
+ @VisibleForTesting
Set<Long> getKnownIdsForTest() {
synchronized (mBlobsLock) {
return mKnownBlobIds;
@@ -246,7 +271,7 @@
@GuardedBy("mBlobsLock")
private void addSessionForUserLocked(BlobStoreSession session, int userId) {
getUserSessionsLocked(userId).put(session.getSessionId(), session);
- mKnownBlobIds.add(session.getSessionId());
+ addActiveBlobIdLocked(session.getSessionId());
}
@GuardedBy("mBlobsLock")
@@ -258,7 +283,13 @@
private void addBlobForUserLocked(BlobMetadata blobMetadata,
ArrayMap<BlobHandle, BlobMetadata> userBlobs) {
userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata);
- mKnownBlobIds.add(blobMetadata.getBlobId());
+ addActiveBlobIdLocked(blobMetadata.getBlobId());
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void addActiveBlobIdLocked(long id) {
+ mActiveBlobIds.add(id);
+ mKnownBlobIds.add(id);
}
private long createSessionInternal(BlobHandle blobHandle,
@@ -392,7 +423,7 @@
synchronized (mBlobsLock) {
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
- mKnownBlobIds.remove(session.getSessionId());
+ mActiveBlobIds.remove(session.getSessionId());
if (LOGV) {
Slog.v(TAG, "Session is invalid; deleted " + session);
}
@@ -491,6 +522,9 @@
if (sessionsIndexFile == null) {
Slog.wtf(TAG, "Error creating sessions index file");
return;
+ } else if (!sessionsIndexFile.exists()) {
+ Slog.w(TAG, "Sessions index file not available: " + sessionsIndexFile.getBaseFile());
+ return;
}
mSessions.clear();
@@ -580,6 +614,9 @@
if (blobsIndexFile == null) {
Slog.wtf(TAG, "Error creating blobs index file");
return;
+ } else if (!blobsIndexFile.exists()) {
+ Slog.w(TAG, "Blobs index file not available: " + blobsIndexFile.getBaseFile());
+ return;
}
mBlobsMap.clear();
@@ -704,7 +741,7 @@
if (session.getOwnerUid() == uid
&& session.getOwnerPackageName().equals(packageName)) {
session.getSessionFile().delete();
- mKnownBlobIds.remove(session.getSessionId());
+ mActiveBlobIds.remove(session.getSessionId());
indicesToRemove.add(i);
}
}
@@ -724,7 +761,7 @@
// Delete the blob if it doesn't have any active leases.
if (!blobMetadata.hasLeases()) {
blobMetadata.getBlobFile().delete();
- mKnownBlobIds.remove(blobMetadata.getBlobId());
+ mActiveBlobIds.remove(blobMetadata.getBlobId());
indicesToRemove.add(i);
}
}
@@ -747,7 +784,7 @@
for (int i = 0, count = userSessions.size(); i < count; ++i) {
final BlobStoreSession session = userSessions.valueAt(i);
session.getSessionFile().delete();
- mKnownBlobIds.remove(session.getSessionId());
+ mActiveBlobIds.remove(session.getSessionId());
}
}
@@ -757,7 +794,7 @@
for (int i = 0, count = userBlobs.size(); i < count; ++i) {
final BlobMetadata blobMetadata = userBlobs.valueAt(i);
blobMetadata.getBlobFile().delete();
- mKnownBlobIds.remove(blobMetadata.getBlobId());
+ mActiveBlobIds.remove(blobMetadata.getBlobId());
}
}
if (LOGV) {
@@ -777,7 +814,7 @@
for (File file : blobsDir.listFiles()) {
try {
final long id = Long.parseLong(file.getName());
- if (mKnownBlobIds.indexOf(id) < 0) {
+ if (mActiveBlobIds.indexOf(id) < 0) {
filesToDelete.add(file);
deletedBlobIds.add(id);
}
@@ -812,7 +849,7 @@
if (shouldRemove) {
blobMetadata.getBlobFile().delete();
- mKnownBlobIds.remove(blobMetadata.getBlobId());
+ mActiveBlobIds.remove(blobMetadata.getBlobId());
deletedBlobIds.add(blobMetadata.getBlobId());
}
return shouldRemove;
@@ -821,12 +858,9 @@
writeBlobsInfoAsync();
// Cleanup any stale sessions.
- final ArrayList<Integer> indicesToRemove = new ArrayList<>();
for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
- indicesToRemove.clear();
- for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
- final BlobStoreSession blobStoreSession = userSessions.valueAt(j);
+ userSessions.removeIf((sessionId, blobStoreSession) -> {
boolean shouldRemove = false;
// Cleanup sessions which haven't been modified in a while.
@@ -842,14 +876,11 @@
if (shouldRemove) {
blobStoreSession.getSessionFile().delete();
- mKnownBlobIds.remove(blobStoreSession.getSessionId());
- indicesToRemove.add(j);
+ mActiveBlobIds.remove(blobStoreSession.getSessionId());
deletedBlobIds.add(blobStoreSession.getSessionId());
}
- }
- for (int j = 0; j < indicesToRemove.size(); ++j) {
- userSessions.removeAt(indicesToRemove.get(j));
- }
+ return shouldRemove;
+ });
}
if (LOGV) {
Slog.v(TAG, "Completed idle maintenance; deleted "
@@ -889,7 +920,7 @@
}
blobMetadata.getBlobFile().delete();
userBlobs.remove(blobHandle);
- mKnownBlobIds.remove(blobMetadata.getBlobId());
+ mActiveBlobIds.remove(blobMetadata.getBlobId());
writeBlobsInfoAsync();
}
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index b96161a..4c98b5f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -77,11 +77,11 @@
/**
* @hide
- * @deprecated use {@link #getReasonCodeDescription(int)}
*/
- @Deprecated
- public static String getReasonName(int reason) {
- switch (reason) {
+ // TODO(142420609): make it @SystemApi for mainline
+ @NonNull
+ public static String getReasonCodeDescription(int reasonCode) {
+ switch (reasonCode) {
case REASON_CANCELED: return "canceled";
case REASON_CONSTRAINTS_NOT_SATISFIED: return "constraints";
case REASON_PREEMPT: return "preempt";
@@ -89,7 +89,7 @@
case REASON_DEVICE_IDLE: return "device_idle";
case REASON_DEVICE_THERMAL: return "thermal";
case REASON_RESTRAINED: return "restrained";
- default: return "unknown:" + reason;
+ default: return "unknown:" + reasonCode;
}
}
@@ -100,13 +100,6 @@
return JOB_STOP_REASON_CODES;
}
- /** @hide */
- // @SystemApi TODO make it a system api for mainline
- @NonNull
- public static String getReasonCodeDescription(int reasonCode) {
- return getReasonName(reasonCode);
- }
-
@UnsupportedAppUsage
private final int jobId;
private final PersistableBundle extras;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
index e28e5bd..d050347 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java
@@ -359,7 +359,8 @@
}
pw.print(pe.stopReasons.valueAt(k));
pw.print("x ");
- pw.print(JobParameters.getReasonName(pe.stopReasons.keyAt(k)));
+ pw.print(JobParameters
+ .getReasonCodeDescription(pe.stopReasons.keyAt(k)));
}
pw.println();
}
@@ -606,8 +607,9 @@
if (reason != null) {
pw.print(mEventReasons[index]);
} else {
- pw.print(JobParameters.getReasonName((mEventCmds[index] & EVENT_STOP_REASON_MASK)
- >> EVENT_STOP_REASON_SHIFT));
+ pw.print(JobParameters.getReasonCodeDescription(
+ (mEventCmds[index] & EVENT_STOP_REASON_MASK)
+ >> EVENT_STOP_REASON_SHIFT));
}
}
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ff7944d..c1e529f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1963,7 +1963,7 @@
if (restriction != null) {
final int reason = restriction.getReason();
serviceContext.cancelExecutingJobLocked(reason,
- "restricted due to " + JobParameters.getReasonName(reason));
+ "restricted due to " + JobParameters.getReasonCodeDescription(reason));
}
}
}
@@ -3110,7 +3110,7 @@
final JobRestriction restriction = mJobRestrictions.get(i);
if (restriction.isJobRestricted(job)) {
final int reason = restriction.getReason();
- pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]");
+ pw.print(" " + JobParameters.getReasonCodeDescription(reason));
}
}
} else {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 789f20b..b63cc19 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -39,6 +39,7 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.job.GrantedUriPermissions;
@@ -97,6 +98,15 @@
| CONSTRAINT_IDLE;
/**
+ * Standard media URIs that contain the media files that might be important to the user.
+ * @see #mHasMediaBackupExemption
+ */
+ private static final Uri[] MEDIA_URIS_FOR_STANDBY_EXEMPTION = {
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+ };
+
+ /**
* The constraints that we want to log to statsd.
*
* Constraints that can be inferred from other atoms have been excluded to avoid logging too
@@ -441,13 +451,13 @@
if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_DEADLINE;
}
- boolean mediaOnly = false;
+ boolean exemptedMediaUrisOnly = false;
if (job.getTriggerContentUris() != null) {
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
- mediaOnly = true;
+ exemptedMediaUrisOnly = true;
for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) {
- if (!MediaStore.AUTHORITY.equals(uri.getUri().getAuthority())) {
- mediaOnly = false;
+ if (!ArrayUtils.contains(MEDIA_URIS_FOR_STANDBY_EXEMPTION, uri.getUri())) {
+ exemptedMediaUrisOnly = false;
break;
}
}
@@ -475,8 +485,8 @@
job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid);
}
final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
- mHasMediaBackupExemption = !job.hasLateConstraint() && mediaOnly && requiresNetwork
- && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
+ mHasMediaBackupExemption = !job.hasLateConstraint() && exemptedMediaUrisOnly
+ && requiresNetwork && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
}
/** Copy constructor: used specifically when cloning JobStatus objects for persistence,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index bf61eb4..a4ab31d 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -65,6 +65,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.CrossProfileAppsInternal;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -283,6 +284,12 @@
* start is the first usage of the app
*/
long mInitialForegroundServiceStartTimeoutMillis;
+ /**
+ * User usage that would elevate an app's standby bucket will also elevate the standby bucket of
+ * cross profile connected apps. Explicit standby bucket setting via
+ * {@link #setAppStandbyBucket(String, int, int, int, int)} will not be propagated.
+ */
+ boolean mLinkCrossProfileApps;
private volatile boolean mAppIdleEnabled;
private boolean mIsCharging;
@@ -445,10 +452,12 @@
continue;
}
if (!packageName.equals(providerPkgName)) {
+ final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName,
+ userId);
synchronized (mAppIdleLock) {
- reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
- REASON_SUB_USAGE_SYNC_ADAPTER, elapsedRealtime,
- mSyncAdapterTimeoutMillis);
+ reportNoninteractiveUsageCrossUserLocked(packageName, userId,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
+ elapsedRealtime, mSyncAdapterTimeoutMillis, linkedProfiles);
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -477,10 +486,10 @@
}
final long elapsedRealtime = mInjector.elapsedRealtime();
-
+ final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
synchronized (mAppIdleLock) {
- reportNoninteractiveUsageLocked(packageName, userId, bucketToPromote,
- usageReason, elapsedRealtime, durationMillis);
+ reportNoninteractiveUsageCrossUserLocked(packageName, userId, bucketToPromote,
+ usageReason, elapsedRealtime, durationMillis, linkedProfiles);
}
}
@@ -492,10 +501,11 @@
final int currentBucket =
mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
if (currentBucket == STANDBY_BUCKET_NEVER) {
+ final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
// Bring the app out of the never bucket
- reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_WORKING_SET,
- REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED, elapsedRealtime,
- mUnexemptedSyncScheduledTimeoutMillis);
+ reportNoninteractiveUsageCrossUserLocked(packageName, userId,
+ STANDBY_BUCKET_WORKING_SET, REASON_SUB_USAGE_UNEXEMPTED_SYNC_SCHEDULED,
+ elapsedRealtime, mUnexemptedSyncScheduledTimeoutMillis, linkedProfiles);
}
}
}
@@ -504,14 +514,39 @@
if (!mAppIdleEnabled) return;
final long elapsedRealtime = mInjector.elapsedRealtime();
-
+ final List<UserHandle> linkedProfiles = getCrossProfileTargets(packageName, userId);
synchronized (mAppIdleLock) {
- reportNoninteractiveUsageLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
+ reportNoninteractiveUsageCrossUserLocked(packageName, userId, STANDBY_BUCKET_ACTIVE,
REASON_SUB_USAGE_EXEMPTED_SYNC_START, elapsedRealtime,
- mExemptedSyncStartTimeoutMillis);
+ mExemptedSyncStartTimeoutMillis, linkedProfiles);
}
}
+ /**
+ * Helper method to report indirect user usage of an app and handle reporting the usage
+ * against cross profile connected apps. <br>
+ * Use {@link #reportNoninteractiveUsageLocked(String, int, int, int, long, long)} if
+ * cross profile connected apps do not need to be handled.
+ */
+ private void reportNoninteractiveUsageCrossUserLocked(String packageName, int userId,
+ int bucket, int subReason, long elapsedRealtime, long nextCheckDelay,
+ List<UserHandle> otherProfiles) {
+ reportNoninteractiveUsageLocked(packageName, userId, bucket, subReason, elapsedRealtime,
+ nextCheckDelay);
+ final int size = otherProfiles.size();
+ for (int profileIndex = 0; profileIndex < size; profileIndex++) {
+ final int otherUserId = otherProfiles.get(profileIndex).getIdentifier();
+ reportNoninteractiveUsageLocked(packageName, otherUserId, bucket, subReason,
+ elapsedRealtime, nextCheckDelay);
+ }
+ }
+
+ /**
+ * Helper method to report indirect user usage of an app. <br>
+ * Use
+ * {@link #reportNoninteractiveUsageCrossUserLocked(String, int, int, int, long, long, List)}
+ * if cross profile connected apps need to be handled.
+ */
private void reportNoninteractiveUsageLocked(String packageName, int userId, int bucket,
int subReason, long elapsedRealtime, long nextCheckDelay) {
final AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId, bucket,
@@ -766,8 +801,16 @@
|| eventType == UsageEvents.Event.SLICE_PINNED
|| eventType == UsageEvents.Event.SLICE_PINNED_PRIV
|| eventType == UsageEvents.Event.FOREGROUND_SERVICE_START)) {
+ final String pkg = event.getPackageName();
+ final List<UserHandle> linkedProfiles = getCrossProfileTargets(pkg, userId);
synchronized (mAppIdleLock) {
- reportEventLocked(event.getPackageName(), eventType, elapsedRealtime, userId);
+ reportEventLocked(pkg, eventType, elapsedRealtime, userId);
+
+ final int size = linkedProfiles.size();
+ for (int profileIndex = 0; profileIndex < size; profileIndex++) {
+ final int linkedUserId = linkedProfiles.get(profileIndex).getIdentifier();
+ reportEventLocked(pkg, eventType, elapsedRealtime, linkedUserId);
+ }
}
}
}
@@ -826,6 +869,16 @@
}
}
+ /**
+ * Note: don't call this with the lock held since it makes calls to other system services.
+ */
+ private @NonNull List<UserHandle> getCrossProfileTargets(String pkg, int userId) {
+ synchronized (mAppIdleLock) {
+ if (!mLinkCrossProfileApps) return Collections.emptyList();
+ }
+ return mInjector.getValidCrossProfileTargets(pkg, userId);
+ }
+
private int usageEventToSubReason(int eventType) {
switch (eventType) {
case UsageEvents.Event.ACTIVITY_RESUMED: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
@@ -1589,6 +1642,7 @@
private PackageManagerInternal mPackageManagerInternal;
private DisplayManager mDisplayManager;
private PowerManager mPowerManager;
+ private CrossProfileAppsInternal mCrossProfileAppsInternal;
int mBootPhase;
/**
* The minimum amount of time required since the last user interaction before an app can be
@@ -1620,6 +1674,8 @@
Context.DISPLAY_SERVICE);
mPowerManager = mContext.getSystemService(PowerManager.class);
mBatteryManager = mContext.getSystemService(BatteryManager.class);
+ mCrossProfileAppsInternal = LocalServices.getService(
+ CrossProfileAppsInternal.class);
final ActivityManager activityManager =
(ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
@@ -1727,6 +1783,17 @@
public boolean isDeviceIdleMode() {
return mPowerManager.isDeviceIdleMode();
}
+
+ public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
+ final int uid = mPackageManagerInternal.getPackageUidInternal(pkg, 0, userId);
+ if (uid < 0
+ || !mPackageManagerInternal.getPackage(uid).isCrossProfile()
+ || !mCrossProfileAppsInternal
+ .verifyUidHasInteractAcrossProfilePermission(pkg, uid)) {
+ return Collections.emptyList();
+ }
+ return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
+ }
}
class AppStandbyHandler extends Handler {
@@ -1857,6 +1924,8 @@
"initial_foreground_service_start_duration";
private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS =
"auto_restricted_bucket_delay_ms";
+ private static final String KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS =
+ "cross_profile_apps_share_standby_buckets";
public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
@@ -1868,6 +1937,7 @@
public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY;
+ public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1973,6 +2043,10 @@
mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
COMPRESS_TIME
? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
+
+ mLinkCrossProfileApps = mParser.getBoolean(
+ KEY_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS,
+ DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS);
}
// Check if app_idle_enabled has changed. Do this after getting the rest of the settings
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 7d18578..11d3a68 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -56,6 +56,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -375,13 +376,13 @@
}
/**
- * Thrown if all extractors implementations provided to {@link #create} failed to sniff the
- * input content.
+ * Thrown if all parser implementations provided to {@link #create} failed to sniff the input
+ * content.
*/
public static final class UnrecognizedInputFormatException extends IOException {
/**
- * Creates a new instance which signals that the extractors with the given names failed to
+ * Creates a new instance which signals that the parsers with the given names failed to
* parse the input.
*/
@NonNull
@@ -389,7 +390,7 @@
private static UnrecognizedInputFormatException createForExtractors(
@NonNull String... extractorNames) {
StringBuilder builder = new StringBuilder();
- builder.append("None of the available extractors ( ");
+ builder.append("None of the available parsers ( ");
builder.append(extractorNames[0]);
for (int i = 1; i < extractorNames.length; i++) {
builder.append(", ");
@@ -404,17 +405,149 @@
}
}
+ // Public constants.
+
+ /**
+ * Sets whether constant bitrate seeking should be enabled for exo.AdtsParser. {@code boolean}
+ * expected. Default value is {@code false}.
+ */
+ public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING =
+ "exo.AdtsParser.enableCbrSeeking";
+ /**
+ * Sets whether constant bitrate seeking should be enabled for exo.AmrParser. {@code boolean}
+ * expected. Default value is {@code false}.
+ */
+ public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "exo.AmrParser.enableCbrSeeking";
+ /**
+ * Sets whether the ID3 track should be disabled for exo.FlacParser. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_FLAC_DISABLE_ID3 = "exo.FlacParser.disableId3";
+ /**
+ * Sets whether exo.FragmentedMp4Parser should ignore edit lists. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_FMP4_IGNORE_EDIT_LISTS =
+ "exo.FragmentedMp4Parser.ignoreEditLists";
+ /**
+ * Sets whether exo.FragmentedMp4Parser should ignore the tfdt box. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_FMP4_IGNORE_TFDT_BOX =
+ "exo.FragmentedMp4Parser.ignoreTfdtBox";
+ /**
+ * Sets whether exo.FragmentedMp4Parser should treat all video frames as key frames. {@code
+ * boolean} expected. Default value is {@code false}.
+ */
+ public static final String PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES =
+ "exo.FragmentedMp4Parser.treatVideoFramesAsKeyframes";
+ /**
+ * Sets whether exo.MatroskaParser should avoid seeking to the cues element. {@code boolean}
+ * expected. Default value is {@code false}.
+ *
+ * <p>If this flag is enabled and the cues element occurs after the first cluster, then the
+ * media is treated as unseekable.
+ */
+ public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING =
+ "exo.MatroskaParser.disableCuesSeeking";
+ /**
+ * Sets whether the ID3 track should be disabled for exo.Mp3Parser. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_MP3_DISABLE_ID3 = "exo.Mp3Parser.disableId3";
+ /**
+ * Sets whether constant bitrate seeking should be enabled for exo.Mp3Parser. {@code boolean}
+ * expected. Default value is {@code false}.
+ */
+ public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "exo.Mp3Parser.enableCbrSeeking";
+ /**
+ * Sets whether exo.Mp3Parser should generate a time-to-byte mapping. {@code boolean} expected.
+ * Default value is {@code false}.
+ *
+ * <p>Enabling this flag may require to scan a significant portion of the file to compute a seek
+ * point. Therefore, it should only be used if:
+ *
+ * <ul>
+ * <li>the file is small, or
+ * <li>the bitrate is variable (or the type of bitrate is unknown) and the seeking metadata
+ * provided in the file is not precise enough (or is not present).
+ * </ul>
+ */
+ public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING =
+ "exo.Mp3Parser.enableIndexSeeking";
+ /**
+ * Sets whether exo.Mp4Parser should ignore edit lists. {@code boolean} expected. Default value
+ * is {@code false}.
+ */
+ public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "exo.Mp4Parser.ignoreEditLists";
+ /**
+ * Sets the operation mode for exo.TsParser. {@code String} expected. Valid values are {@code
+ * "single_pmt"}, {@code "multi_pmt"}, and {@code "hls"}. Default value is {@code "single_pmt"}.
+ *
+ * <p>The operation modes alter the way exo.TsParser behaves so that it can handle certain kinds
+ * of commonly-occurring malformed media.
+ *
+ * <ul>
+ * <li>{@code "single_pmt"}: Only the first found PMT is parsed. Others are ignored, even if
+ * more PMTs are declared in the PAT.
+ * <li>{@code "multi_pmt"}: Behave as described in ISO/IEC 13818-1.
+ * <li>{@code "hls"}: Enable {@code "single_pmt"} mode, and ignore continuity counters.
+ * </ul>
+ */
+ public static final String PARAMETER_TS_MODE = "exo.TsParser.mode";
+ /**
+ * Sets whether exo.TsParser should treat samples consisting of non-IDR I slices as
+ * synchronization samples (key-frames). {@code boolean} expected. Default value is {@code
+ * false}.
+ */
+ public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES =
+ "exo.TsParser.allowNonIdrAvcKeyframes";
+ /**
+ * Sets whether exo.TsParser should ignore AAC elementary streams. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "exo.TsParser.ignoreAacStream";
+ /**
+ * Sets whether exo.TsParser should ignore AVC elementary streams. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "exo.TsParser.ignoreAvcStream";
+ /**
+ * Sets whether exo.TsParser should ignore splice information streams. {@code boolean} expected.
+ * Default value is {@code false}.
+ */
+ public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM =
+ "exo.TsParser.ignoreSpliceInfoStream";
+ /**
+ * Sets whether exo.TsParser should split AVC stream into access units based on slice headers.
+ * {@code boolean} expected. Default value is {@code false}.
+ *
+ * <p>This flag should be left disabled if the stream contains access units delimiters in order
+ * to avoid unnecessary computational costs.
+ */
+ public static final String PARAMETER_TS_DETECT_ACCESS_UNITS =
+ "exo.TsParser.ignoreDetectAccessUnits";
+ /**
+ * Sets whether exo.TsParser should handle HDMV DTS audio streams. {@code boolean} expected.
+ * Default value is {@code false}.
+ *
+ * <p>Enabling this flag will disable the detection of SCTE subtitles.
+ */
+ public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS =
+ "exo.TsParser.enableHdmvDtsAudioStreams";
+
// Private constants.
private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
+ private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME;
// Instance creation methods.
/**
- * Creates an instance backed by the extractor with the given {@code name}. The returned
- * instance will attempt extraction without sniffing the content.
+ * Creates an instance backed by the parser with the given {@code name}. The returned instance
+ * will attempt parsing without sniffing the content.
*
- * @param name The name of the extractor that will be associated with the created instance.
+ * @param name The name of the parser that will be associated with the created instance.
* @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed.
* @return A new instance.
* @throws IllegalArgumentException If an invalid name is provided.
@@ -428,42 +561,43 @@
}
/**
- * Creates an instance whose backing extractor will be selected by sniffing the content during
- * the first {@link #advance} call. Extractor implementations will sniff the content in order of
- * appearance in {@code extractorNames}.
+ * Creates an instance whose backing parser will be selected by sniffing the content during the
+ * first {@link #advance} call. Parser implementations will sniff the content in order of
+ * appearance in {@code parserNames}.
*
* @param outputConsumer The {@link OutputConsumer} to which extracted data is output.
- * @param extractorNames The names of the extractors to sniff the content with. If empty, a
- * default array of names is used.
+ * @param parserNames The names of the parsers to sniff the content with. If empty, a default
+ * array of names is used.
* @return A new instance.
*/
@NonNull
public static MediaParser create(
- @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) {
- assertValidNames(extractorNames);
- if (extractorNames.length == 0) {
- extractorNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
+ @NonNull OutputConsumer outputConsumer, @NonNull String... parserNames) {
+ assertValidNames(parserNames);
+ if (parserNames.length == 0) {
+ parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
}
- return new MediaParser(outputConsumer, /* sniff= */ true, extractorNames);
+ return new MediaParser(outputConsumer, /* sniff= */ true, parserNames);
}
// Misc static methods.
/**
- * Returns an immutable list with the names of the extractors that are suitable for container
+ * Returns an immutable list with the names of the parsers that are suitable for container
* formats with the given {@link MediaFormat}.
*
* <p>TODO: List which properties are taken into account. E.g. MimeType.
*/
@NonNull
- public static List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
+ public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
throw new UnsupportedOperationException();
}
// Private fields.
+ private final Map<String, Object> mParserParameters;
private final OutputConsumer mOutputConsumer;
- private final String[] mExtractorNamesPool;
+ private final String[] mParserNamesPool;
private final PositionHolder mPositionHolder;
private final InputReadingDataSource mDataSource;
private final ExtractorInputAdapter mScratchExtractorInputAdapter;
@@ -477,18 +611,63 @@
// Public methods.
/**
- * Returns the name of the backing extractor implementation.
+ * Sets parser-specific parameters which allow customizing behavior.
+ *
+ * <p>Must be called before the first call to {@link #advance}.
+ *
+ * @param parameterName The name of the parameter to set. See {@code PARAMETER_*} constants for
+ * documentation on possible values.
+ * @param value The value to set for the given {@code parameterName}. See {@code PARAMETER_*}
+ * constants for documentation on the expected types.
+ * @return This instance, for convenience.
+ * @throws IllegalStateException If called after calling {@link #advance} on the same instance.
+ */
+ @NonNull
+ public MediaParser setParameter(@NonNull String parameterName, @NonNull Object value) {
+ if (mExtractor != null) {
+ throw new IllegalStateException(
+ "setParameters() must be called before the first advance() call.");
+ }
+ Class expectedType = EXPECTED_TYPE_BY_PARAMETER_NAME.get(parameterName);
+ // Ignore parameter names that are not contained in the map, in case the client is passing
+ // a parameter that is being added in a future version of this library.
+ if (expectedType != null && !expectedType.isInstance(value)) {
+ throw new IllegalArgumentException(
+ parameterName
+ + " expects a "
+ + expectedType.getSimpleName()
+ + " but a "
+ + value.getClass().getSimpleName()
+ + " was passed.");
+ }
+ mParserParameters.put(parameterName, value);
+ return this;
+ }
+
+ /**
+ * Returns whether the given {@code parameterName} is supported by this parser.
+ *
+ * @param parameterName The parameter name to check support for. One of the {@code PARAMETER_*}
+ * constants.
+ * @return Whether the given {@code parameterName} is supported.
+ */
+ public boolean supportsParameter(@NonNull String parameterName) {
+ return EXPECTED_TYPE_BY_PARAMETER_NAME.containsKey(parameterName);
+ }
+
+ /**
+ * Returns the name of the backing parser implementation.
*
* <p>If this instance was creating using {@link #createByName}, the provided name is returned.
* If this instance was created using {@link #create}, this method will return null until the
- * first call to {@link #advance}, after which the name of the backing extractor implementation
- * is returned.
+ * first call to {@link #advance}, after which the name of the backing parser implementation is
+ * returned.
*
- * @return The name of the backing extractor implementation, or null if the backing extractor
+ * @return The name of the backing parser implementation, or null if the backing parser
* implementation has not yet been selected.
*/
@Nullable
- public String getExtractorName() {
+ public String getParserName() {
return mExtractorName;
}
@@ -499,7 +678,7 @@
* <p>This method will block until some progress has been made.
*
* <p>If this instance was created using {@link #create}. the first call to this method will
- * sniff the content with the extractors with the provided names.
+ * sniff the content with the parsers with the provided names.
*
* @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media
* container data.
@@ -520,13 +699,15 @@
}
mDataSource.mInputReader = seekableInputReader;
- if (mExtractor == null) {
- for (String extractorName : mExtractorNamesPool) {
- Extractor extractor =
- EXTRACTOR_FACTORIES_BY_NAME.get(extractorName).createInstance();
+ // TODO: Apply parameters when creating extractor instances.
+ if (mExtractorName != null) {
+ mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+ } else if (mExtractor == null) {
+ for (String parserName : mParserNamesPool) {
+ Extractor extractor = EXTRACTOR_FACTORIES_BY_NAME.get(parserName).createInstance();
try {
if (extractor.sniff(mExtractorInput)) {
- mExtractorName = extractorName;
+ mExtractorName = parserName;
mExtractor = extractor;
mExtractor.init(new ExtractorOutputAdapter());
break;
@@ -540,7 +721,7 @@
}
}
if (mExtractor == null) {
- throw UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool);
+ throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool);
}
return true;
}
@@ -586,22 +767,22 @@
* Releases any acquired resources.
*
* <p>After calling this method, this instance becomes unusable and no other methods should be
- * invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing.
+ * invoked.
*/
public void release() {
+ // TODO: Dump media metrics here.
mExtractorInput = null;
mExtractor = null;
}
// Private methods.
- private MediaParser(
- OutputConsumer outputConsumer, boolean sniff, String... extractorNamesPool) {
+ private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) {
+ mParserParameters = new HashMap<>();
mOutputConsumer = outputConsumer;
- mExtractorNamesPool = extractorNamesPool;
+ mParserNamesPool = parserNamesPool;
if (!sniff) {
- mExtractorName = extractorNamesPool[0];
- mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+ mExtractorName = parserNamesPool[0];
}
mPositionHolder = new PositionHolder();
mDataSource = new InputReadingDataSource();
@@ -921,7 +1102,7 @@
throw new IllegalArgumentException(
"Invalid extractor name: "
+ name
- + ". Supported extractors are: "
+ + ". Supported parsers are: "
+ TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet())
+ ".");
}
@@ -933,20 +1114,42 @@
static {
// Using a LinkedHashMap to keep the insertion order when iterating over the keys.
LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
- extractorFactoriesByName.put("exo.Ac3Extractor", Ac3Extractor::new);
- extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new);
- extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new);
- extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new);
- extractorFactoriesByName.put("exo.FlacExtractor", FlacExtractor::new);
- extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new);
- extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new);
- extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new);
- extractorFactoriesByName.put("exo.Mp3Extractor", Mp3Extractor::new);
- extractorFactoriesByName.put("exo.Mp4Extractor", Mp4Extractor::new);
- extractorFactoriesByName.put("exo.OggExtractor", OggExtractor::new);
- extractorFactoriesByName.put("exo.PsExtractor", PsExtractor::new);
- extractorFactoriesByName.put("exo.TsExtractor", TsExtractor::new);
- extractorFactoriesByName.put("exo.WavExtractor", WavExtractor::new);
+ extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new);
+ extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new);
+ extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new);
+ extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new);
+ extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new);
+ extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new);
+ extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new);
+ extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new);
+ extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new);
+ extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new);
+ extractorFactoriesByName.put("exo.OggParser", OggExtractor::new);
+ extractorFactoriesByName.put("exo.PsParser", PsExtractor::new);
+ extractorFactoriesByName.put("exo.TsParser", TsExtractor::new);
+ extractorFactoriesByName.put("exo.WavParser", WavExtractor::new);
EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
+
+ HashMap<String, Class> expectedTypeByParameterName = new HashMap<>();
+ expectedTypeByParameterName.put(PARAMETER_ADTS_ENABLE_CBR_SEEKING, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_AMR_ENABLE_CBR_SEEKING, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_FLAC_DISABLE_ID3, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_FMP4_IGNORE_EDIT_LISTS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_FMP4_IGNORE_TFDT_BOX, Boolean.class);
+ expectedTypeByParameterName.put(
+ PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MP3_DISABLE_ID3, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_CBR_SEEKING, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MP3_ENABLE_INDEX_SEEKING, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_MP4_IGNORE_EDIT_LISTS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_MODE, String.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AAC_STREAM, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_AVC_STREAM, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_DETECT_ACCESS_UNITS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, Boolean.class);
+ EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName);
}
}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
index 6c7f82a..0d163cf 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
@@ -40,7 +40,7 @@
* @return the runtime permissions read
*/
@Nullable
- RuntimePermissionsState read(@NonNull UserHandle user);
+ RuntimePermissionsState readAsUser(@NonNull UserHandle user);
/**
* Write the runtime permissions to persistence.
@@ -50,7 +50,7 @@
* @param runtimePermissions the runtime permissions to write
* @param user the user to write for
*/
- void write(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user);
+ void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions, @NonNull UserHandle user);
/**
* Delete the runtime permissions from persistence.
@@ -59,7 +59,7 @@
*
* @param user the user to delete for
*/
- void delete(@NonNull UserHandle user);
+ void deleteAsUser(@NonNull UserHandle user);
/**
* Create a new instance of {@link RuntimePermissionsPersistence} implementation.
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
index 90b1c4b..205ffc2 100644
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
@@ -67,7 +67,7 @@
@Nullable
@Override
- public RuntimePermissionsState read(@NonNull UserHandle user) {
+ public RuntimePermissionsState readAsUser(@NonNull UserHandle user) {
File file = getFile(user);
try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
XmlPullParser parser = Xml.newPullParser();
@@ -172,7 +172,7 @@
}
@Override
- public void write(@NonNull RuntimePermissionsState runtimePermissions,
+ public void writeAsUser(@NonNull RuntimePermissionsState runtimePermissions,
@NonNull UserHandle user) {
File file = getFile(user);
AtomicFile atomicFile = new AtomicFile(file);
@@ -252,7 +252,7 @@
}
@Override
- public void delete(@NonNull UserHandle user) {
+ public void deleteAsUser(@NonNull UserHandle user) {
getFile(user).delete();
}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
index 2908a38..64d6545 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
@@ -40,7 +40,7 @@
* @return the roles read
*/
@Nullable
- RolesState read(@NonNull UserHandle user);
+ RolesState readAsUser(@NonNull UserHandle user);
/**
* Write the roles to persistence.
@@ -50,7 +50,7 @@
* @param roles the roles to write
* @param user the user to write for
*/
- void write(@NonNull RolesState roles, @NonNull UserHandle user);
+ void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user);
/**
* Delete the roles from persistence.
@@ -59,7 +59,7 @@
*
* @param user the user to delete for
*/
- void delete(@NonNull UserHandle user);
+ void deleteAsUser(@NonNull UserHandle user);
/**
* Create a new instance of {@link RolesPersistence} implementation.
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
index 06fad77..3031c82 100644
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
@@ -65,7 +65,7 @@
@Nullable
@Override
- public RolesState read(@NonNull UserHandle user) {
+ public RolesState readAsUser(@NonNull UserHandle user) {
File file = getFile(user);
try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
XmlPullParser parser = Xml.newPullParser();
@@ -146,8 +146,7 @@
}
@Override
- public void write(@NonNull RolesState roles,
- @NonNull UserHandle user) {
+ public void writeAsUser(@NonNull RolesState roles, @NonNull UserHandle user) {
File file = getFile(user);
AtomicFile atomicFile = new AtomicFile(file);
FileOutputStream outputStream = null;
@@ -206,7 +205,7 @@
}
@Override
- public void delete(@NonNull UserHandle user) {
+ public void deleteAsUser(@NonNull UserHandle user) {
getFile(user).delete();
}
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index 4c5c2b2..25765af 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -28,7 +28,6 @@
name: "com.android.sdkext-defaults",
java_libs: [ "framework-sdkextensions" ],
prebuilts: [
- "com.android.sdkext.ldconfig",
"derive_sdk.rc",
],
key: "com.android.sdkext.key",
@@ -51,13 +50,6 @@
certificate: "com.android.sdkext",
}
-prebuilt_etc {
- name: "com.android.sdkext.ldconfig",
- src: "ld.config.txt",
- filename: "ld.config.txt",
- installable: false,
-}
-
python_binary_host {
name: "gen_sdkinfo",
srcs: [
diff --git a/apex/sdkextensions/ld.config.txt b/apex/sdkextensions/ld.config.txt
deleted file mode 100644
index dcc69b8..0000000
--- a/apex/sdkextensions/ld.config.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Bionic loader config file for the sdkextensions apex.
-
-dir.sdkextensions = /apex/com.android.sdkext/bin/
-
-[sdkextensions]
-additional.namespaces = platform
-
-namespace.default.isolated = true
-namespace.default.links = platform
-namespace.default.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "platform" namespace: used for NDK libraries
-###############################################################################
-namespace.platform.isolated = true
-namespace.platform.search.paths = /system/${LIB}
-namespace.platform.asan.search.paths = /data/asan/system/${LIB}
-
-# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
-# Add /apex/... path to the permitted paths because linker uses realpath(3)
-# to check the accessibility of the lib. We could add this to search.paths
-# instead but that makes the resolution of bionic libs be dependent on
-# the order of /system/lib and /apex/... in search.paths. If /apex/...
-# is after /system/lib, then /apex/... is never tried because libc.so
-# is always found in /system/lib but fails to pass the accessibility test
-# because of its realpath. It's better to not depend on the ordering if
-# possible.
-namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
-namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 0e93110..2f3e2ac 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -21,14 +21,16 @@
apex_defaults {
native_shared_libs: [
"libstats_jni",
+ "libstatspull",
+ "libstatssocket",
],
- // binaries: ["vold"],
+ binaries: ["statsd"],
java_libs: [
"framework-statsd",
"service-statsd",
],
compile_multilib: "both",
- // prebuilts: ["my_prebuilt"],
+ prebuilts: ["com.android.os.statsd.init.rc"],
name: "com.android.os.statsd-defaults",
key: "com.android.os.statsd.key",
certificate: ":com.android.os.statsd.certificate",
@@ -47,6 +49,12 @@
certificate: "com.android.os.statsd",
}
+prebuilt_etc {
+ name: "com.android.os.statsd.init.rc",
+ src: "statsd.rc",
+ filename: "init.rc",
+ installable: false,
+}
// JNI library for StatsLog.write
cc_library_shared {
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index 526d17f..e637187 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -159,6 +159,9 @@
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to addConfig in statsmanager");
+ throw new StatsUnavailableException(e.getMessage(), e);
}
}
}
@@ -195,6 +198,9 @@
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to removeConfig in statsmanager");
+ throw new StatsUnavailableException(e.getMessage(), e);
}
}
}
@@ -391,6 +397,9 @@
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to getReports in statsmanager");
+ throw new StatsUnavailableException(e.getMessage(), e);
}
}
}
@@ -428,6 +437,9 @@
throw new StatsUnavailableException("could not connect", e);
} catch (SecurityException e) {
throw new StatsUnavailableException(e.getMessage(), e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to getStatsMetadata in statsmanager");
+ throw new StatsUnavailableException(e.getMessage(), e);
}
}
}
@@ -469,6 +481,9 @@
+ "registered experiment IDs");
}
throw new StatsUnavailableException("could not connect", e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to getRegisteredExperimentIds in statsmanager");
+ throw new StatsUnavailableException(e.getMessage(), e);
}
}
}
diff --git a/cmds/statsd/statsd.rc b/apex/statsd/statsd.rc
similarity index 67%
rename from cmds/statsd/statsd.rc
rename to apex/statsd/statsd.rc
index a98ecd5..605da2a 100644
--- a/cmds/statsd/statsd.rc
+++ b/apex/statsd/statsd.rc
@@ -12,19 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-service statsd /system/bin/statsd
+service statsd /apex/com.android.os.statsd/bin/statsd
class main
socket statsdw dgram+passcred 0222 statsd statsd
user statsd
group statsd log
writepid /dev/cpuset/system-background/tasks
-
-on property:ro.statsd.enable=false
- stop statsd
-
-on post-fs-data
- # Create directory for statsd
- mkdir /data/misc/stats-data/ 0770 statsd system
- mkdir /data/misc/stats-service/ 0770 statsd system
- mkdir /data/misc/stats-active-metric/ 0770 statsd system
- mkdir /data/misc/train-info/ 0770 statsd system
diff --git a/api/current.txt b/api/current.txt
index 702744d..0a7bac5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -32,6 +32,7 @@
field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
+ field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS";
field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
@@ -2983,7 +2984,6 @@
method public int describeContents();
method public static String feedbackTypeToString(int);
method public static String flagToString(int);
- method public int getAnimatedImageRes();
method @Deprecated public boolean getCanRetrieveWindowContent();
method public int getCapabilities();
method @Deprecated public String getDescription();
@@ -2992,6 +2992,7 @@
method public int getNonInteractiveUiTimeoutMillis();
method public android.content.pm.ResolveInfo getResolveInfo();
method public String getSettingsActivityName();
+ method @Nullable public android.graphics.drawable.Drawable loadAnimatedImage(@NonNull android.content.Context);
method public String loadDescription(android.content.pm.PackageManager);
method @Nullable public String loadHtmlDescription(@NonNull android.content.pm.PackageManager);
method public CharSequence loadSummary(android.content.pm.PackageManager);
@@ -6969,7 +6970,6 @@
method public boolean removeOverrideApn(@NonNull android.content.ComponentName, int);
method public boolean removeUser(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method public boolean requestBugreport(@NonNull android.content.ComponentName);
- method public void requestSetLocationProviderAllowed(@NonNull android.content.ComponentName, @NonNull String, boolean);
method @Deprecated public boolean resetPassword(String, int);
method public boolean resetPasswordWithToken(@NonNull android.content.ComponentName, String, byte[], int);
method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long);
@@ -12328,6 +12328,7 @@
method public int describeContents();
method public void dump(android.util.Printer, String);
method public final int getIconResource();
+ method public boolean isCrossProfileIntentForwarderActivity();
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
@@ -12466,6 +12467,7 @@
method @NonNull public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(int);
method public boolean isRateLimitingActive();
method public boolean isRequestPinShortcutSupported();
+ method public void pushDynamicShortcut(@NonNull android.content.pm.ShortcutInfo);
method public void removeAllDynamicShortcuts();
method public void removeDynamicShortcuts(@NonNull java.util.List<java.lang.String>);
method public void removeLongLivedShortcuts(@NonNull java.util.List<java.lang.String>);
@@ -23175,7 +23177,7 @@
method public void onConfigureWindow(android.view.Window, boolean, boolean);
method public android.view.View onCreateCandidatesView();
method public android.view.View onCreateExtractTextView();
- method @Nullable public android.view.inputmethod.InlineSuggestionsRequest onCreateInlineSuggestionsRequest();
+ method @Nullable public android.view.inputmethod.InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull android.os.Bundle);
method public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
method public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
method public android.view.View onCreateInputView();
@@ -24786,7 +24788,7 @@
}
public abstract class DrmInitData {
- method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
+ method @Deprecated public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
method @NonNull public android.media.DrmInitData.SchemeInitData getSchemeInitDataAt(int);
method public int getSchemeInitDataCount();
}
@@ -25305,6 +25307,10 @@
method public void recycle();
}
+ public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException {
+ ctor public MediaCodec.IncompatibleWithBlockModelException();
+ }
+
public static final class MediaCodec.LinearBlock {
method protected void finalize();
method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
@@ -25332,23 +25338,24 @@
}
public static final class MediaCodec.OutputFrame {
- method public void getChangedKeys(@NonNull java.util.Set<java.lang.String>);
method public int getFlags();
method @NonNull public android.media.MediaFormat getFormat();
method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method public long getPresentationTimeUs();
+ method public void retrieveChangedKeys(@NonNull java.util.Set<java.lang.String>);
}
public final class MediaCodec.QueueRequest {
method public void queue();
method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
- method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
- method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock, long, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock);
method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
- method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, long, int);
+ method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
+ method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long);
method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
}
@@ -26402,10 +26409,30 @@
method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException, java.lang.InterruptedException;
method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
- method @Nullable public String getExtractorName();
- method @NonNull public static java.util.List<java.lang.String> getExtractorNames(@NonNull android.media.MediaFormat);
+ method @Nullable public String getParserName();
+ method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat);
method public void release();
method public void seek(@NonNull android.media.MediaParser.SeekPoint);
+ method @NonNull public android.media.MediaParser setParameter(@NonNull String, @NonNull Object);
+ method public boolean supportsParameter(@NonNull String);
+ field public static final String PARAMETER_ADTS_ENABLE_CBR_SEEKING = "exo.AdtsParser.enableCbrSeeking";
+ field public static final String PARAMETER_AMR_ENABLE_CBR_SEEKING = "exo.AmrParser.enableCbrSeeking";
+ field public static final String PARAMETER_FLAC_DISABLE_ID3 = "exo.FlacParser.disableId3";
+ field public static final String PARAMETER_FMP4_IGNORE_EDIT_LISTS = "exo.FragmentedMp4Parser.ignoreEditLists";
+ field public static final String PARAMETER_FMP4_IGNORE_TFDT_BOX = "exo.FragmentedMp4Parser.ignoreTfdtBox";
+ field public static final String PARAMETER_FMP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES = "exo.FragmentedMp4Parser.treatVideoFramesAsKeyframes";
+ field public static final String PARAMETER_MATROSKA_DISABLE_CUES_SEEKING = "exo.MatroskaParser.disableCuesSeeking";
+ field public static final String PARAMETER_MP3_DISABLE_ID3 = "exo.Mp3Parser.disableId3";
+ field public static final String PARAMETER_MP3_ENABLE_CBR_SEEKING = "exo.Mp3Parser.enableCbrSeeking";
+ field public static final String PARAMETER_MP3_ENABLE_INDEX_SEEKING = "exo.Mp3Parser.enableIndexSeeking";
+ field public static final String PARAMETER_MP4_IGNORE_EDIT_LISTS = "exo.Mp4Parser.ignoreEditLists";
+ field public static final String PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES = "exo.TsParser.allowNonIdrAvcKeyframes";
+ field public static final String PARAMETER_TS_DETECT_ACCESS_UNITS = "exo.TsParser.ignoreDetectAccessUnits";
+ field public static final String PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS = "exo.TsParser.enableHdmvDtsAudioStreams";
+ field public static final String PARAMETER_TS_IGNORE_AAC_STREAM = "exo.TsParser.ignoreAacStream";
+ field public static final String PARAMETER_TS_IGNORE_AVC_STREAM = "exo.TsParser.ignoreAvcStream";
+ field public static final String PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM = "exo.TsParser.ignoreSpliceInfoStream";
+ field public static final String PARAMETER_TS_MODE = "exo.TsParser.mode";
}
public static interface MediaParser.InputReader {
@@ -26872,21 +26899,27 @@
ctor public MediaRoute2ProviderService();
method @NonNull public final java.util.List<android.media.RoutingSessionInfo> getAllSessionInfo();
method @Nullable public final android.media.RoutingSessionInfo getSessionInfo(@NonNull String);
+ method public final void notifyRequestFailed(long, int);
method public final void notifyRoutes(@NonNull java.util.Collection<android.media.MediaRoute2Info>);
method public final void notifySessionCreated(@NonNull android.media.RoutingSessionInfo, long);
method public final void notifySessionCreationFailed(long);
method public final void notifySessionReleased(@NonNull String);
method public final void notifySessionUpdated(@NonNull android.media.RoutingSessionInfo);
method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
- method public abstract void onCreateSession(@NonNull String, @NonNull String, long, @Nullable android.os.Bundle);
- method public abstract void onDeselectRoute(@NonNull String, @NonNull String);
+ method public abstract void onCreateSession(long, @NonNull String, @NonNull String, @Nullable android.os.Bundle);
+ method public abstract void onDeselectRoute(long, @NonNull String, @NonNull String);
method public void onDiscoveryPreferenceChanged(@NonNull android.media.RouteDiscoveryPreference);
- method public abstract void onReleaseSession(@NonNull String);
- method public abstract void onSelectRoute(@NonNull String, @NonNull String);
- method public abstract void onSetRouteVolume(@NonNull String, int);
- method public abstract void onSetSessionVolume(@NonNull String, int);
- method public abstract void onTransferToRoute(@NonNull String, @NonNull String);
- field public static final long REQUEST_ID_UNKNOWN = 0L; // 0x0L
+ method public abstract void onReleaseSession(long, @NonNull String);
+ method public abstract void onSelectRoute(long, @NonNull String, @NonNull String);
+ method public abstract void onSetRouteVolume(long, @NonNull String, int);
+ method public abstract void onSetSessionVolume(long, @NonNull String, int);
+ method public abstract void onTransferToRoute(long, @NonNull String, @NonNull String);
+ field public static final int REASON_INVALID_COMMAND = 4; // 0x4
+ field public static final int REASON_NETWORK_ERROR = 2; // 0x2
+ field public static final int REASON_REJECTED = 1; // 0x1
+ field public static final int REASON_ROUTE_NOT_AVAILABLE = 3; // 0x3
+ field public static final int REASON_UNKNOWN_ERROR = 0; // 0x0
+ field public static final long REQUEST_ID_NONE = 0L; // 0x0L
field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
}
@@ -43399,6 +43432,7 @@
method @NonNull public abstract java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherFor(@NonNull java.util.List<java.lang.String>);
method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForAllAvailable();
method @Nullable public java.util.concurrent.Flow.Publisher<android.service.controls.Control> publisherForSuggested();
+ method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control);
field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService";
field @NonNull public static final String TAG = "ControlsProviderService";
}
@@ -45645,7 +45679,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final String getCallerDisplayName();
method public final int getCallerDisplayNamePresentation();
- method public int getCallerNumberVerificationStatus();
+ method public final int getCallerNumberVerificationStatus();
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
@@ -45698,7 +45732,7 @@
method public final void setAudioModeIsVoip(boolean);
method public final void setAudioRoute(int);
method public final void setCallerDisplayName(String, int);
- method public void setCallerNumberVerificationStatus(int);
+ method public final void setCallerNumberVerificationStatus(int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
@@ -46550,8 +46584,8 @@
field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
field public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
- field public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL = "allow_holding_video_call";
field public static final String KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL = "allow_hold_call_during_emergency_bool";
+ field public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL = "allow_hold_video_call_bool";
field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
@@ -47065,6 +47099,350 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ClosedSubscriberGroupInfo> CREATOR;
}
+ public final class DataFailCause {
+ field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab
+ field public static final int ACCESS_BLOCK = 2087; // 0x827
+ field public static final int ACCESS_BLOCK_ALL = 2088; // 0x828
+ field public static final int ACCESS_CLASS_DSAC_REJECTION = 2108; // 0x83c
+ field public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 2128; // 0x850
+ field public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 48; // 0x30
+ field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
+ field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
+ field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
+ field public static final int APN_DISABLED = 2045; // 0x7fd
+ field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b
+ field public static final int APN_MISMATCH = 2054; // 0x806
+ field public static final int APN_PARAMETERS_CHANGED = 2060; // 0x80c
+ field public static final int APN_PENDING_HANDOVER = 2041; // 0x7f9
+ field public static final int APN_TYPE_CONFLICT = 112; // 0x70
+ field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a
+ field public static final int BEARER_HANDLING_NOT_SUPPORTED = 60; // 0x3c
+ field public static final int CALL_DISALLOWED_IN_ROAMING = 2068; // 0x814
+ field public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 127; // 0x7f
+ field public static final int CANNOT_ENCODE_OTA_MESSAGE = 2159; // 0x86f
+ field public static final int CDMA_ALERT_STOP = 2077; // 0x81d
+ field public static final int CDMA_INCOMING_CALL = 2076; // 0x81c
+ field public static final int CDMA_INTERCEPT = 2073; // 0x819
+ field public static final int CDMA_LOCK = 2072; // 0x818
+ field public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 2075; // 0x81b
+ field public static final int CDMA_REORDER = 2074; // 0x81a
+ field public static final int CDMA_RETRY_ORDER = 2086; // 0x826
+ field public static final int CHANNEL_ACQUISITION_FAILURE = 2078; // 0x81e
+ field public static final int CLOSE_IN_PROGRESS = 2030; // 0x7ee
+ field public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 56; // 0x38
+ field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76
+ field public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 2083; // 0x823
+ field public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 2091; // 0x82b
+ field public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 2080; // 0x820
+ field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+ field public static final int CONGESTION = 2106; // 0x83a
+ field public static final int CONNECTION_RELEASED = 2113; // 0x841
+ field public static final int CS_DOMAIN_NOT_AVAILABLE = 2181; // 0x885
+ field public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 2188; // 0x88c
+ field public static final int DATA_PLAN_EXPIRED = 2198; // 0x896
+ field public static final int DATA_ROAMING_SETTINGS_DISABLED = 2064; // 0x810
+ field public static final int DATA_SETTINGS_DISABLED = 2063; // 0x80f
+ field public static final int DBM_OR_SMS_IN_PROGRESS = 2211; // 0x8a3
+ field public static final int DDS_SWITCHED = 2065; // 0x811
+ field public static final int DDS_SWITCH_IN_PROGRESS = 2067; // 0x813
+ field public static final int DRB_RELEASED_BY_RRC = 2112; // 0x840
+ field public static final int DS_EXPLICIT_DEACTIVATION = 2125; // 0x84d
+ field public static final int DUAL_SWITCH = 2227; // 0x8b3
+ field public static final int DUN_CALL_DISALLOWED = 2056; // 0x808
+ field public static final int DUPLICATE_BEARER_ID = 2118; // 0x846
+ field public static final int EHRPD_TO_HRPD_FALLBACK = 2049; // 0x801
+ field public static final int EMBMS_NOT_ENABLED = 2193; // 0x891
+ field public static final int EMBMS_REGULAR_DEACTIVATION = 2195; // 0x893
+ field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74
+ field public static final int EMERGENCY_MODE = 2221; // 0x8ad
+ field public static final int EMM_ACCESS_BARRED = 115; // 0x73
+ field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79
+ field public static final int EMM_ATTACH_FAILED = 2115; // 0x843
+ field public static final int EMM_ATTACH_STARTED = 2116; // 0x844
+ field public static final int EMM_DETACHED = 2114; // 0x842
+ field public static final int EMM_T3417_EXPIRED = 2130; // 0x852
+ field public static final int EMM_T3417_EXT_EXPIRED = 2131; // 0x853
+ field public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 2178; // 0x882
+ field public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 2179; // 0x883
+ field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+ field public static final int ESM_BAD_OTA_MESSAGE = 2122; // 0x84a
+ field public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 2120; // 0x848
+ field public static final int ESM_COLLISION_SCENARIOS = 2119; // 0x847
+ field public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 2124; // 0x84c
+ field public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 2123; // 0x84b
+ field public static final int ESM_FAILURE = 2182; // 0x886
+ field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35
+ field public static final int ESM_LOCAL_CAUSE_NONE = 2126; // 0x84e
+ field public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 2121; // 0x849
+ field public static final int ESM_PROCEDURE_TIME_OUT = 2155; // 0x86b
+ field public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 2111; // 0x83f
+ field public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 2201; // 0x899
+ field public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 2200; // 0x898
+ field public static final int EVDO_HDR_CHANGED = 2202; // 0x89a
+ field public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 2206; // 0x89e
+ field public static final int EVDO_HDR_EXITED = 2203; // 0x89b
+ field public static final int EVDO_HDR_NO_SESSION = 2204; // 0x89c
+ field public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 2205; // 0x89d
+ field public static final int FADE = 2217; // 0x8a9
+ field public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 2207; // 0x89f
+ field public static final int FEATURE_NOT_SUPP = 40; // 0x28
+ field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c
+ field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d
+ field public static final int FORBIDDEN_APN_NAME = 2066; // 0x812
+ field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe
+ field public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 2097; // 0x831
+ field public static final int GPRS_SERVICES_NOT_ALLOWED = 2098; // 0x832
+ field public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 2103; // 0x837
+ field public static final int HANDOFF_PREFERENCE_CHANGED = 2251; // 0x8cb
+ field public static final int HDR_ACCESS_FAILURE = 2213; // 0x8a5
+ field public static final int HDR_FADE = 2212; // 0x8a4
+ field public static final int HDR_NO_LOCK_GRANTED = 2210; // 0x8a2
+ field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78
+ field public static final int IFACE_MISMATCH = 117; // 0x75
+ field public static final int ILLEGAL_ME = 2096; // 0x830
+ field public static final int ILLEGAL_MS = 2095; // 0x82f
+ field public static final int IMEI_NOT_ACCEPTED = 2177; // 0x881
+ field public static final int IMPLICITLY_DETACHED = 2100; // 0x834
+ field public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 2176; // 0x880
+ field public static final int INCOMING_CALL_REJECTED = 2092; // 0x82c
+ field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a
+ field public static final int INTERFACE_IN_USE = 2058; // 0x80a
+ field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72
+ field public static final int INTERNAL_EPC_NONEPC_TRANSITION = 2057; // 0x809
+ field public static final int INVALID_CONNECTION_ID = 2156; // 0x86c
+ field public static final int INVALID_DNS_ADDR = 123; // 0x7b
+ field public static final int INVALID_EMM_STATE = 2190; // 0x88e
+ field public static final int INVALID_MANDATORY_INFO = 96; // 0x60
+ field public static final int INVALID_MODE = 2223; // 0x8af
+ field public static final int INVALID_PCSCF_ADDR = 113; // 0x71
+ field public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 124; // 0x7c
+ field public static final int INVALID_PRIMARY_NSAPI = 2158; // 0x86e
+ field public static final int INVALID_SIM_STATE = 2224; // 0x8b0
+ field public static final int INVALID_TRANSACTION_ID = 81; // 0x51
+ field public static final int IPV6_ADDRESS_TRANSFER_FAILED = 2047; // 0x7ff
+ field public static final int IPV6_PREFIX_UNAVAILABLE = 2250; // 0x8ca
+ field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77
+ field public static final int IP_VERSION_MISMATCH = 2055; // 0x807
+ field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892
+ field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829
+ field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba
+ field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb
+ field public static final int LLC_SNDCP = 25; // 0x19
+ field public static final int LOCAL_END = 2215; // 0x8a7
+ field public static final int LOCATION_AREA_NOT_ALLOWED = 2102; // 0x836
+ field public static final int LOST_CONNECTION = 65540; // 0x10004
+ field public static final int LOWER_LAYER_REGISTRATION_FAILURE = 2197; // 0x895
+ field public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 2044; // 0x7fc
+ field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845
+ field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f
+ field public static final int MAC_FAILURE = 2183; // 0x887
+ field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d
+ field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876
+ field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f
+ field public static final int MAX_IPV4_CONNECTIONS = 2052; // 0x804
+ field public static final int MAX_IPV6_CONNECTIONS = 2053; // 0x805
+ field public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe
+ field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f
+ field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61
+ field public static final int MIP_CONFIG_FAILURE = 2050; // 0x802
+ field public static final int MIP_FA_ADMIN_PROHIBITED = 2001; // 0x7d1
+ field public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 2012; // 0x7dc
+ field public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 2008; // 0x7d8
+ field public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 2004; // 0x7d4
+ field public static final int MIP_FA_INSUFFICIENT_RESOURCES = 2002; // 0x7d2
+ field public static final int MIP_FA_MALFORMED_REPLY = 2007; // 0x7d7
+ field public static final int MIP_FA_MALFORMED_REQUEST = 2006; // 0x7d6
+ field public static final int MIP_FA_MISSING_CHALLENGE = 2017; // 0x7e1
+ field public static final int MIP_FA_MISSING_HOME_ADDRESS = 2015; // 0x7df
+ field public static final int MIP_FA_MISSING_HOME_AGENT = 2014; // 0x7de
+ field public static final int MIP_FA_MISSING_NAI = 2013; // 0x7dd
+ field public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2003; // 0x7d3
+ field public static final int MIP_FA_REASON_UNSPECIFIED = 2000; // 0x7d0
+ field public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 2005; // 0x7d5
+ field public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 2011; // 0x7db
+ field public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 2010; // 0x7da
+ field public static final int MIP_FA_STALE_CHALLENGE = 2018; // 0x7e2
+ field public static final int MIP_FA_UNKNOWN_CHALLENGE = 2016; // 0x7e0
+ field public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 2009; // 0x7d9
+ field public static final int MIP_HA_ADMIN_PROHIBITED = 2020; // 0x7e4
+ field public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 2029; // 0x7ed
+ field public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 2023; // 0x7e7
+ field public static final int MIP_HA_INSUFFICIENT_RESOURCES = 2021; // 0x7e5
+ field public static final int MIP_HA_MALFORMED_REQUEST = 2025; // 0x7e9
+ field public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2022; // 0x7e6
+ field public static final int MIP_HA_REASON_UNSPECIFIED = 2019; // 0x7e3
+ field public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 2024; // 0x7e8
+ field public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 2028; // 0x7ec
+ field public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 2027; // 0x7eb
+ field public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 2026; // 0x7ea
+ field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b
+ field public static final int MODEM_APP_PREEMPTED = 2032; // 0x7f0
+ field public static final int MODEM_RESTART = 2037; // 0x7f5
+ field public static final int MSC_TEMPORARILY_NOT_REACHABLE = 2180; // 0x884
+ field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65
+ field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62
+ field public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 2099; // 0x833
+ field public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 2192; // 0x890
+ field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37
+ field public static final int NAS_LAYER_FAILURE = 2191; // 0x88f
+ field public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 2167; // 0x877
+ field public static final int NAS_SIGNALLING = 14; // 0xe
+ field public static final int NETWORK_FAILURE = 38; // 0x26
+ field public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 2154; // 0x86a
+ field public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 2153; // 0x869
+ field public static final int NETWORK_INITIATED_TERMINATION = 2031; // 0x7ef
+ field public static final int NONE = 0; // 0x0
+ field public static final int NON_IP_NOT_SUPPORTED = 2069; // 0x815
+ field public static final int NORMAL_RELEASE = 2218; // 0x8aa
+ field public static final int NO_CDMA_SERVICE = 2084; // 0x824
+ field public static final int NO_COLLOCATED_HDR = 2225; // 0x8b1
+ field public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 2189; // 0x88d
+ field public static final int NO_GPRS_CONTEXT = 2094; // 0x82e
+ field public static final int NO_HYBRID_HDR_SERVICE = 2209; // 0x8a1
+ field public static final int NO_PDP_CONTEXT_ACTIVATED = 2107; // 0x83b
+ field public static final int NO_RESPONSE_FROM_BASE_STATION = 2081; // 0x821
+ field public static final int NO_SERVICE = 2216; // 0x8a8
+ field public static final int NO_SERVICE_ON_GATEWAY = 2093; // 0x82d
+ field public static final int NSAPI_IN_USE = 35; // 0x23
+ field public static final int NULL_APN_DISALLOWED = 2061; // 0x80d
+ field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001
+ field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a
+ field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b
+ field public static final int OEM_DCFAILCAUSE_12 = 4108; // 0x100c
+ field public static final int OEM_DCFAILCAUSE_13 = 4109; // 0x100d
+ field public static final int OEM_DCFAILCAUSE_14 = 4110; // 0x100e
+ field public static final int OEM_DCFAILCAUSE_15 = 4111; // 0x100f
+ field public static final int OEM_DCFAILCAUSE_2 = 4098; // 0x1002
+ field public static final int OEM_DCFAILCAUSE_3 = 4099; // 0x1003
+ field public static final int OEM_DCFAILCAUSE_4 = 4100; // 0x1004
+ field public static final int OEM_DCFAILCAUSE_5 = 4101; // 0x1005
+ field public static final int OEM_DCFAILCAUSE_6 = 4102; // 0x1006
+ field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007
+ field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008
+ field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009
+ field public static final int ONLY_IPV4V6_ALLOWED = 57; // 0x39
+ field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32
+ field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33
+ field public static final int ONLY_NON_IP_ALLOWED = 58; // 0x3a
+ field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34
+ field public static final int OPERATOR_BARRED = 8; // 0x8
+ field public static final int OTASP_COMMIT_IN_PROGRESS = 2208; // 0x8a0
+ field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36
+ field public static final int PDN_INACTIVITY_TIMER_EXPIRED = 2051; // 0x803
+ field public static final int PDN_IPV4_CALL_DISALLOWED = 2033; // 0x7f1
+ field public static final int PDN_IPV4_CALL_THROTTLED = 2034; // 0x7f2
+ field public static final int PDN_IPV6_CALL_DISALLOWED = 2035; // 0x7f3
+ field public static final int PDN_IPV6_CALL_THROTTLED = 2036; // 0x7f4
+ field public static final int PDN_NON_IP_CALL_DISALLOWED = 2071; // 0x817
+ field public static final int PDN_NON_IP_CALL_THROTTLED = 2070; // 0x816
+ field public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 2109; // 0x83d
+ field public static final int PDP_DUPLICATE = 2104; // 0x838
+ field public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 2161; // 0x871
+ field public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 2163; // 0x873
+ field public static final int PDP_LOWERLAYER_ERROR = 2164; // 0x874
+ field public static final int PDP_MODIFY_COLLISION = 2165; // 0x875
+ field public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 2162; // 0x872
+ field public static final int PDP_PPP_NOT_SUPPORTED = 2038; // 0x7f6
+ field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e
+ field public static final int PHONE_IN_USE = 2222; // 0x8ae
+ field public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 2040; // 0x7f8
+ field public static final int PLMN_NOT_ALLOWED = 2101; // 0x835
+ field public static final int PPP_AUTH_FAILURE = 2229; // 0x8b5
+ field public static final int PPP_CHAP_FAILURE = 2232; // 0x8b8
+ field public static final int PPP_CLOSE_IN_PROGRESS = 2233; // 0x8b9
+ field public static final int PPP_OPTION_MISMATCH = 2230; // 0x8b6
+ field public static final int PPP_PAP_FAILURE = 2231; // 0x8b7
+ field public static final int PPP_TIMEOUT = 2228; // 0x8b4
+ field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc
+ field public static final int PROFILE_BEARER_INCOMPATIBLE = 2042; // 0x7fa
+ field public static final int PROTOCOL_ERRORS = 111; // 0x6f
+ field public static final int QOS_NOT_ACCEPTED = 37; // 0x25
+ field public static final int RADIO_ACCESS_BEARER_FAILURE = 2110; // 0x83e
+ field public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 2160; // 0x870
+ field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001
+ field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb
+ field public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 2220; // 0x8ac
+ field public static final int REGISTRATION_FAIL = -1; // 0xffffffff
+ field public static final int REGULAR_DEACTIVATION = 36; // 0x24
+ field public static final int REJECTED_BY_BASE_STATION = 2082; // 0x822
+ field public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 2173; // 0x87d
+ field public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 2174; // 0x87e
+ field public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 2171; // 0x87b
+ field public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 2175; // 0x87f
+ field public static final int RRC_CONNECTION_ABORT_REQUEST = 2151; // 0x867
+ field public static final int RRC_CONNECTION_ACCESS_BARRED = 2139; // 0x85b
+ field public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 2137; // 0x859
+ field public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 2138; // 0x85a
+ field public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 2144; // 0x860
+ field public static final int RRC_CONNECTION_CELL_RESELECTION = 2140; // 0x85c
+ field public static final int RRC_CONNECTION_CONFIG_FAILURE = 2141; // 0x85d
+ field public static final int RRC_CONNECTION_INVALID_REQUEST = 2168; // 0x878
+ field public static final int RRC_CONNECTION_LINK_FAILURE = 2143; // 0x85f
+ field public static final int RRC_CONNECTION_NORMAL_RELEASE = 2147; // 0x863
+ field public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 2150; // 0x866
+ field public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 2148; // 0x864
+ field public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 2149; // 0x865
+ field public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 2146; // 0x862
+ field public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 2172; // 0x87c
+ field public static final int RRC_CONNECTION_RF_UNAVAILABLE = 2170; // 0x87a
+ field public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 2152; // 0x868
+ field public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 2145; // 0x861
+ field public static final int RRC_CONNECTION_TIMER_EXPIRED = 2142; // 0x85e
+ field public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 2169; // 0x879
+ field public static final int RRC_UPLINK_CONNECTION_RELEASE = 2134; // 0x856
+ field public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 2132; // 0x854
+ field public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 2133; // 0x855
+ field public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 2136; // 0x858
+ field public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 2135; // 0x857
+ field public static final int RUIM_NOT_PRESENT = 2085; // 0x825
+ field public static final int SECURITY_MODE_REJECTED = 2186; // 0x88a
+ field public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 2129; // 0x851
+ field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21
+ field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20
+ field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
+ field public static final int SIGNAL_LOST = -3; // 0xfffffffd
+ field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb
+ field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888
+ field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894
+ field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
+ field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29
+ field public static final int TFT_SYTAX_ERROR = 42; // 0x2a
+ field public static final int THERMAL_EMERGENCY = 2090; // 0x82a
+ field public static final int THERMAL_MITIGATION = 2062; // 0x80e
+ field public static final int TRAT_SWAP_FAILED = 2048; // 0x800
+ field public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 128; // 0x80
+ field public static final int UE_IS_ENTERING_POWERSAVE_MODE = 2226; // 0x8b2
+ field public static final int UE_RAT_CHANGE = 2105; // 0x839
+ field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889
+ field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897
+ field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
+ field public static final int UNACCEPTABLE_NETWORK_PARAMETER = 65538; // 0x10002
+ field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b
+ field public static final int UNKNOWN = 65536; // 0x10000
+ field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
+ field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c
+ field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b
+ field public static final int UNPREFERRED_RAT = 2039; // 0x7f7
+ field public static final int UNSUPPORTED_1X_PREV = 2214; // 0x8a6
+ field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42
+ field public static final int UNSUPPORTED_QCI_VALUE = 59; // 0x3b
+ field public static final int USER_AUTHENTICATION = 29; // 0x1d
+ field public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 2245; // 0x8c5
+ field public static final int VSNCP_APN_UNAUTHORIZED = 2238; // 0x8be
+ field public static final int VSNCP_GEN_ERROR = 2237; // 0x8bd
+ field public static final int VSNCP_INSUFFICIENT_PARAMETERS = 2243; // 0x8c3
+ field public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 2240; // 0x8c0
+ field public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 2248; // 0x8c8
+ field public static final int VSNCP_PDN_GATEWAY_REJECT = 2242; // 0x8c2
+ field public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 2241; // 0x8c1
+ field public static final int VSNCP_PDN_ID_IN_USE = 2246; // 0x8c6
+ field public static final int VSNCP_PDN_LIMIT_EXCEEDED = 2239; // 0x8bf
+ field public static final int VSNCP_RECONNECT_NOT_ALLOWED = 2249; // 0x8c9
+ field public static final int VSNCP_RESOURCE_UNAVAILABLE = 2244; // 0x8c4
+ field public static final int VSNCP_SUBSCRIBER_LIMITATION = 2247; // 0x8c7
+ field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc
+ }
+
public final class DisplayInfo implements android.os.Parcelable {
method public int describeContents();
method public int getNetworkType();
@@ -47227,32 +47605,8 @@
public final class PhoneCapability implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.List<java.lang.Integer> getBands(int);
- method @NonNull public java.util.List<java.util.List<java.lang.Long>> getConcurrentFeaturesSupport();
- method @NonNull public java.util.List<java.lang.String> getLogicalModemUuids();
- method public int getMaxActiveDedicatedBearers();
- method public int getMaxActiveInternetData();
- method public int getMaxActivePsVoice();
- method public long getPsDataConnectionLingerTimeMillis();
- method @NonNull public java.util.List<android.telephony.SimSlotCapability> getSimSlotCapabilities();
- method public long getSupportedRats();
- method public int getUeCategory(boolean, int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
- field public static final long MODEM_FEATURE_3GPP2_REG = 1L; // 0x1L
- field public static final long MODEM_FEATURE_3GPP_REG = 2L; // 0x2L
- field public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 4L; // 0x4L
- field public static final long MODEM_FEATURE_CSIM = 8192L; // 0x2000L
- field public static final long MODEM_FEATURE_CS_VOICE_SESSION = 512L; // 0x200L
- field public static final long MODEM_FEATURE_DEDICATED_BEARER = 2048L; // 0x800L
- field public static final long MODEM_FEATURE_EUTRAN_REG = 32L; // 0x20L
- field public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 128L; // 0x80L
- field public static final long MODEM_FEATURE_GERAN_REG = 8L; // 0x8L
- field public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1024L; // 0x400L
- field public static final long MODEM_FEATURE_NETWORK_SCAN = 4096L; // 0x1000L
- field public static final long MODEM_FEATURE_NGRAN_REG = 64L; // 0x40L
- field public static final long MODEM_FEATURE_PS_VOICE_REG = 256L; // 0x100L
- field public static final long MODEM_FEATURE_UTRAN_REG = 16L; // 0x10L
}
public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
@@ -47442,18 +47796,6 @@
field public static final int INVALID = 2147483647; // 0x7fffffff
}
- public final class SimSlotCapability implements android.os.Parcelable {
- method public int describeContents();
- method public int getPhysicalSlotIndex();
- method public int getSlotType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SimSlotCapability> CREATOR;
- field public static final int SLOT_TYPE_EUICC = 3; // 0x3
- field public static final int SLOT_TYPE_IUICC = 2; // 0x2
- field public static final int SLOT_TYPE_SOFT_SIM = 4; // 0x4
- field public static final int SLOT_TYPE_UICC = 1; // 0x1
- }
-
public final class SmsManager {
method public String createAppSpecificSmsToken(android.app.PendingIntent);
method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
@@ -47810,7 +48152,6 @@
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.PhoneCapability getPhoneCapability();
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
@@ -56419,7 +56760,7 @@
method public int describeContents();
method @NonNull public android.util.Size getMaxSize();
method @NonNull public android.util.Size getMinSize();
- method @Nullable public String getStyle();
+ method @Nullable public android.os.Bundle getStyle();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inline.InlinePresentationSpec> CREATOR;
}
@@ -56427,7 +56768,7 @@
public static final class InlinePresentationSpec.Builder {
ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
method @NonNull public android.view.inline.InlinePresentationSpec build();
- method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@Nullable String);
+ method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@Nullable android.os.Bundle);
}
}
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 7b66f73..6863221 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -30,7 +30,6 @@
}
public class TetheringConstants {
- ctor public TetheringConstants();
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
index 6e59596..56f7a02 100644
--- a/api/module-lib-lint-baseline.txt
+++ b/api/module-lib-lint-baseline.txt
@@ -27,7 +27,3 @@
Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
-
-
-StaticUtils: android.net.TetheringConstants:
- Fully-static utility classes must not have constructor
diff --git a/api/system-current.txt b/api/system-current.txt
index 202a939..2d5655a 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -137,7 +137,6 @@
field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
- field public static final String MONITOR_DEVICE_CONFIG_ACCESS = "android.permission.MONITOR_DEVICE_CONFIG_ACCESS";
field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -631,10 +630,10 @@
method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence);
method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int);
method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed();
+ method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean isValidLockPasswordComplexity(int, @NonNull byte[], int);
method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int);
method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean);
- method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean validateLockPasswordComplexity(boolean, @NonNull byte[], int);
}
public class Notification implements android.os.Parcelable {
@@ -1959,6 +1958,11 @@
method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
}
+ public static final class IntegrityFormula.SourceStamp {
+ method @NonNull public static android.content.integrity.IntegrityFormula notTrusted();
+ method @NonNull public static android.content.integrity.IntegrityFormula stampCertificateHashEquals(@NonNull String);
+ }
+
public final class Rule implements android.os.Parcelable {
ctor public Rule(@NonNull android.content.integrity.IntegrityFormula, int);
method public int describeContents();
@@ -3776,8 +3780,14 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
+ field public static final long FUNCTION_ADB = 1L; // 0x1L
+ field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
+ field public static final long FUNCTION_MIDI = 8L; // 0x8L
+ field public static final long FUNCTION_MTP = 4L; // 0x4L
field public static final long FUNCTION_NCM = 1024L; // 0x400L
field public static final long FUNCTION_NONE = 0L; // 0x0L
+ field public static final long FUNCTION_PTP = 16L; // 0x10L
field public static final long FUNCTION_RNDIS = 32L; // 0x20L
field public static final String USB_CONFIGURED = "configured";
field public static final String USB_CONNECTED = "connected";
@@ -6737,337 +6747,6 @@
}
-package android.net.eap {
-
- public final class EapSessionConfig {
- }
-
- public static final class EapSessionConfig.Builder {
- ctor public EapSessionConfig.Builder();
- method @NonNull public android.net.eap.EapSessionConfig build();
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaConfig(int, int);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaPrimeConfig(int, int, @NonNull String, boolean);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapIdentity(@NonNull byte[]);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapMsChapV2Config(@NonNull String, @NonNull String);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapSimConfig(int, int);
- }
-
- public static class EapSessionConfig.EapAkaConfig extends android.net.eap.EapSessionConfig.EapUiccConfig {
- }
-
- public static class EapSessionConfig.EapAkaPrimeConfig extends android.net.eap.EapSessionConfig.EapAkaConfig {
- method public boolean allowsMismatchedNetworkNames();
- method @NonNull public String getNetworkName();
- }
-
- public abstract static class EapSessionConfig.EapMethodConfig {
- method public int getMethodType();
- }
-
- public static class EapSessionConfig.EapMsChapV2Config extends android.net.eap.EapSessionConfig.EapMethodConfig {
- method @NonNull public String getPassword();
- method @NonNull public String getUsername();
- }
-
- public static class EapSessionConfig.EapSimConfig extends android.net.eap.EapSessionConfig.EapUiccConfig {
- }
-
- public abstract static class EapSessionConfig.EapUiccConfig extends android.net.eap.EapSessionConfig.EapMethodConfig {
- method public int getAppType();
- method public int getSubId();
- }
-
-}
-
-package android.net.ipsec.ike {
-
- public final class ChildSaProposal extends android.net.ipsec.ike.SaProposal {
- }
-
- public static final class ChildSaProposal.Builder {
- ctor public ChildSaProposal.Builder();
- method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addDhGroup(int);
- method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addEncryptionAlgorithm(int, int);
- method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addIntegrityAlgorithm(int);
- method @NonNull public android.net.ipsec.ike.ChildSaProposal build();
- }
-
- public interface ChildSessionCallback {
- method public void onClosed();
- method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
- method public void onIpSecTransformCreated(@NonNull android.net.IpSecTransform, int);
- method public void onIpSecTransformDeleted(@NonNull android.net.IpSecTransform, int);
- method public void onOpened(@NonNull android.net.ipsec.ike.ChildSessionConfiguration);
- }
-
- public final class ChildSessionConfiguration {
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getInboundTrafficSelectors();
- method @NonNull public java.util.List<android.net.LinkAddress> getInternalAddresses();
- method @NonNull public java.util.List<java.net.InetAddress> getInternalDhcpServers();
- method @NonNull public java.util.List<java.net.InetAddress> getInternalDnsServers();
- method @NonNull public java.util.List<android.net.IpPrefix> getInternalSubnets();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors();
- }
-
- public abstract class ChildSessionParams {
- method public long getHardLifetime();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors();
- method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals();
- method public long getSoftLifetime();
- }
-
- public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeFqdnIdentification(@NonNull String);
- field @NonNull public final String fqdn;
- }
-
- public abstract class IkeIdentification {
- }
-
- public final class IkeIpv4AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeIpv4AddrIdentification(@NonNull java.net.Inet4Address);
- field @NonNull public final java.net.Inet4Address ipv4Address;
- }
-
- public class IkeIpv6AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeIpv6AddrIdentification(@NonNull java.net.Inet6Address);
- field @NonNull public final java.net.Inet6Address ipv6Address;
- }
-
- public final class IkeKeyIdIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeKeyIdIdentification(@NonNull byte[]);
- field @NonNull public final byte[] keyId;
- }
-
- public final class IkeRfc822AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeRfc822AddrIdentification(@NonNull String);
- field @NonNull public final String rfc822Name;
- }
-
- public final class IkeSaProposal extends android.net.ipsec.ike.SaProposal {
- method @NonNull public java.util.List<java.lang.Integer> getPseudorandomFunctions();
- }
-
- public static final class IkeSaProposal.Builder {
- ctor public IkeSaProposal.Builder();
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addDhGroup(int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addEncryptionAlgorithm(int, int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addIntegrityAlgorithm(int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addPseudorandomFunction(int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal build();
- }
-
- public final class IkeSession implements java.lang.AutoCloseable {
- ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
- method public void close();
- method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback);
- method public void kill();
- method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback);
- }
-
- public interface IkeSessionCallback {
- method public void onClosed();
- method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
- method public void onError(@NonNull android.net.ipsec.ike.exceptions.IkeProtocolException);
- method public void onOpened(@NonNull android.net.ipsec.ike.IkeSessionConfiguration);
- }
-
- public final class IkeSessionConfiguration {
- method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
- method @NonNull public String getRemoteApplicationVersion();
- method @NonNull public java.util.List<byte[]> getRemoteVendorIDs();
- method public boolean isIkeExtensionEnabled(int);
- field public static final int EXTENSION_TYPE_FRAGMENTATION = 1; // 0x1
- field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2
- }
-
- public final class IkeSessionParams {
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests();
- method public long getHardLifetime();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
- method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig();
- method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals();
- method @NonNull public java.net.InetAddress getServerAddress();
- method public long getSoftLifetime();
- method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket();
- }
-
- public static final class IkeSessionParams.Builder {
- ctor public IkeSessionParams.Builder();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@Nullable java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLifetime(long, long);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
- }
-
- public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
- method @Nullable public java.net.Inet6Address getAddress();
- }
-
- public abstract static class IkeSessionParams.IkeAuthConfig {
- }
-
- public static class IkeSessionParams.IkeAuthDigitalSignLocalConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @NonNull public java.security.cert.X509Certificate getClientEndCertificate();
- method @NonNull public java.util.List<java.security.cert.X509Certificate> getIntermediateCertificates();
- method @NonNull public java.security.PrivateKey getPrivateKey();
- }
-
- public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @Nullable public java.security.cert.X509Certificate getRemoteCaCert();
- }
-
- public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @NonNull public android.net.eap.EapSessionConfig getEapConfig();
- }
-
- public static class IkeSessionParams.IkeAuthPskConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @NonNull public byte[] getPsk();
- }
-
- public static interface IkeSessionParams.IkeConfigRequest {
- }
-
- public final class IkeTrafficSelector {
- ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
- field public final int endPort;
- field @NonNull public final java.net.InetAddress endingAddress;
- field public final int startPort;
- field @NonNull public final java.net.InetAddress startingAddress;
- }
-
- public abstract class SaProposal {
- method @NonNull public java.util.List<java.lang.Integer> getDhGroups();
- method @NonNull public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getEncryptionAlgorithms();
- method @NonNull public java.util.List<java.lang.Integer> getIntegrityAlgorithms();
- field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
- field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
- field public static final int DH_GROUP_NONE = 0; // 0x0
- field public static final int ENCRYPTION_ALGORITHM_3DES = 3; // 0x3
- field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; // 0x13
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; // 0x14
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; // 0x12
- field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
- field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
- field public static final int KEY_LEN_AES_128 = 128; // 0x80
- field public static final int KEY_LEN_AES_192 = 192; // 0xc0
- field public static final int KEY_LEN_AES_256 = 256; // 0x100
- field public static final int KEY_LEN_UNUSED = 0; // 0x0
- field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
- field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
- }
-
- public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
- }
-
- public static final class TransportModeChildSessionParams.Builder {
- ctor public TransportModeChildSessionParams.Builder();
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build();
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder setLifetime(long, long);
- }
-
- public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
- method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests();
- }
-
- public static final class TunnelModeChildSessionParams.Builder {
- ctor public TunnelModeChildSessionParams.Builder();
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet4Address);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet6Address, int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long);
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet6Address getAddress();
- method public int getPrefixLength();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet6Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- }
-
-}
-
-package android.net.ipsec.ike.exceptions {
-
- public abstract class IkeException extends java.lang.Exception {
- }
-
- public final class IkeInternalException extends android.net.ipsec.ike.exceptions.IkeException {
- }
-
- public abstract class IkeProtocolException extends android.net.ipsec.ike.exceptions.IkeException {
- method @Nullable public byte[] getErrorData();
- method public int getErrorType();
- field public static final int ERROR_TYPE_AUTHENTICATION_FAILED = 24; // 0x18
- field public static final int ERROR_TYPE_CHILD_SA_NOT_FOUND = 44; // 0x2c
- field public static final int ERROR_TYPE_FAILED_CP_REQUIRED = 37; // 0x25
- field public static final int ERROR_TYPE_INTERNAL_ADDRESS_FAILURE = 36; // 0x24
- field public static final int ERROR_TYPE_INVALID_IKE_SPI = 4; // 0x4
- field public static final int ERROR_TYPE_INVALID_KE_PAYLOAD = 17; // 0x11
- field public static final int ERROR_TYPE_INVALID_MAJOR_VERSION = 5; // 0x5
- field public static final int ERROR_TYPE_INVALID_MESSAGE_ID = 9; // 0x9
- field public static final int ERROR_TYPE_INVALID_SELECTORS = 39; // 0x27
- field public static final int ERROR_TYPE_INVALID_SYNTAX = 7; // 0x7
- field public static final int ERROR_TYPE_NO_ADDITIONAL_SAS = 35; // 0x23
- field public static final int ERROR_TYPE_NO_PROPOSAL_CHOSEN = 14; // 0xe
- field public static final int ERROR_TYPE_SINGLE_PAIR_REQUIRED = 34; // 0x22
- field public static final int ERROR_TYPE_TEMPORARY_FAILURE = 43; // 0x2b
- field public static final int ERROR_TYPE_TS_UNACCEPTABLE = 38; // 0x26
- field public static final int ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = 1; // 0x1
- }
-
-}
-
package android.net.metrics {
public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
@@ -9078,7 +8757,9 @@
public class UserManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData();
- method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @Nullable String[]) throws android.os.UserManager.UserOperationException;
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
@@ -9086,7 +8767,6 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getUserProfiles(boolean);
method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
@@ -10212,6 +9892,7 @@
public abstract class InlineSuggestionRenderService extends android.app.Service {
ctor public InlineSuggestionRenderService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public android.os.Bundle onGetInlineSuggestionsRendererInfo();
method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int);
field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService";
}
@@ -11389,347 +11070,7 @@
}
public final class DataFailCause {
- field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab
- field public static final int ACCESS_BLOCK = 2087; // 0x827
- field public static final int ACCESS_BLOCK_ALL = 2088; // 0x828
- field public static final int ACCESS_CLASS_DSAC_REJECTION = 2108; // 0x83c
- field public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 2128; // 0x850
- field public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 48; // 0x30
- field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
- field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
- field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
- field public static final int APN_DISABLED = 2045; // 0x7fd
- field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b
- field public static final int APN_MISMATCH = 2054; // 0x806
- field public static final int APN_PARAMETERS_CHANGED = 2060; // 0x80c
- field public static final int APN_PENDING_HANDOVER = 2041; // 0x7f9
- field public static final int APN_TYPE_CONFLICT = 112; // 0x70
- field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a
- field public static final int BEARER_HANDLING_NOT_SUPPORTED = 60; // 0x3c
- field public static final int CALL_DISALLOWED_IN_ROAMING = 2068; // 0x814
- field public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 127; // 0x7f
- field public static final int CANNOT_ENCODE_OTA_MESSAGE = 2159; // 0x86f
- field public static final int CDMA_ALERT_STOP = 2077; // 0x81d
- field public static final int CDMA_INCOMING_CALL = 2076; // 0x81c
- field public static final int CDMA_INTERCEPT = 2073; // 0x819
- field public static final int CDMA_LOCK = 2072; // 0x818
- field public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 2075; // 0x81b
- field public static final int CDMA_REORDER = 2074; // 0x81a
- field public static final int CDMA_RETRY_ORDER = 2086; // 0x826
- field public static final int CHANNEL_ACQUISITION_FAILURE = 2078; // 0x81e
- field public static final int CLOSE_IN_PROGRESS = 2030; // 0x7ee
- field public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 56; // 0x38
- field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76
- field public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 2083; // 0x823
- field public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 2091; // 0x82b
- field public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 2080; // 0x820
- field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
- field public static final int CONGESTION = 2106; // 0x83a
- field public static final int CONNECTION_RELEASED = 2113; // 0x841
- field public static final int CS_DOMAIN_NOT_AVAILABLE = 2181; // 0x885
- field public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 2188; // 0x88c
- field public static final int DATA_PLAN_EXPIRED = 2198; // 0x896
- field public static final int DATA_ROAMING_SETTINGS_DISABLED = 2064; // 0x810
- field public static final int DATA_SETTINGS_DISABLED = 2063; // 0x80f
- field public static final int DBM_OR_SMS_IN_PROGRESS = 2211; // 0x8a3
- field public static final int DDS_SWITCHED = 2065; // 0x811
- field public static final int DDS_SWITCH_IN_PROGRESS = 2067; // 0x813
- field public static final int DRB_RELEASED_BY_RRC = 2112; // 0x840
- field public static final int DS_EXPLICIT_DEACTIVATION = 2125; // 0x84d
- field public static final int DUAL_SWITCH = 2227; // 0x8b3
- field public static final int DUN_CALL_DISALLOWED = 2056; // 0x808
- field public static final int DUPLICATE_BEARER_ID = 2118; // 0x846
- field public static final int EHRPD_TO_HRPD_FALLBACK = 2049; // 0x801
- field public static final int EMBMS_NOT_ENABLED = 2193; // 0x891
- field public static final int EMBMS_REGULAR_DEACTIVATION = 2195; // 0x893
- field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74
- field public static final int EMERGENCY_MODE = 2221; // 0x8ad
- field public static final int EMM_ACCESS_BARRED = 115; // 0x73
- field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79
- field public static final int EMM_ATTACH_FAILED = 2115; // 0x843
- field public static final int EMM_ATTACH_STARTED = 2116; // 0x844
- field public static final int EMM_DETACHED = 2114; // 0x842
- field public static final int EMM_T3417_EXPIRED = 2130; // 0x852
- field public static final int EMM_T3417_EXT_EXPIRED = 2131; // 0x853
- field public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 2178; // 0x882
- field public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 2179; // 0x883
- field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
- field public static final int ESM_BAD_OTA_MESSAGE = 2122; // 0x84a
- field public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 2120; // 0x848
- field public static final int ESM_COLLISION_SCENARIOS = 2119; // 0x847
- field public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 2124; // 0x84c
- field public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 2123; // 0x84b
- field public static final int ESM_FAILURE = 2182; // 0x886
- field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35
- field public static final int ESM_LOCAL_CAUSE_NONE = 2126; // 0x84e
- field public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 2121; // 0x849
- field public static final int ESM_PROCEDURE_TIME_OUT = 2155; // 0x86b
- field public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 2111; // 0x83f
- field public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 2201; // 0x899
- field public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 2200; // 0x898
- field public static final int EVDO_HDR_CHANGED = 2202; // 0x89a
- field public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 2206; // 0x89e
- field public static final int EVDO_HDR_EXITED = 2203; // 0x89b
- field public static final int EVDO_HDR_NO_SESSION = 2204; // 0x89c
- field public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 2205; // 0x89d
- field public static final int FADE = 2217; // 0x8a9
- field public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 2207; // 0x89f
- field public static final int FEATURE_NOT_SUPP = 40; // 0x28
- field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c
- field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d
- field public static final int FORBIDDEN_APN_NAME = 2066; // 0x812
- field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe
- field public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 2097; // 0x831
- field public static final int GPRS_SERVICES_NOT_ALLOWED = 2098; // 0x832
- field public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 2103; // 0x837
- field public static final int HANDOFF_PREFERENCE_CHANGED = 2251; // 0x8cb
- field public static final int HDR_ACCESS_FAILURE = 2213; // 0x8a5
- field public static final int HDR_FADE = 2212; // 0x8a4
- field public static final int HDR_NO_LOCK_GRANTED = 2210; // 0x8a2
- field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78
- field public static final int IFACE_MISMATCH = 117; // 0x75
- field public static final int ILLEGAL_ME = 2096; // 0x830
- field public static final int ILLEGAL_MS = 2095; // 0x82f
- field public static final int IMEI_NOT_ACCEPTED = 2177; // 0x881
- field public static final int IMPLICITLY_DETACHED = 2100; // 0x834
- field public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 2176; // 0x880
- field public static final int INCOMING_CALL_REJECTED = 2092; // 0x82c
- field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a
- field public static final int INTERFACE_IN_USE = 2058; // 0x80a
- field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72
- field public static final int INTERNAL_EPC_NONEPC_TRANSITION = 2057; // 0x809
- field public static final int INVALID_CONNECTION_ID = 2156; // 0x86c
- field public static final int INVALID_DNS_ADDR = 123; // 0x7b
- field public static final int INVALID_EMM_STATE = 2190; // 0x88e
- field public static final int INVALID_MANDATORY_INFO = 96; // 0x60
- field public static final int INVALID_MODE = 2223; // 0x8af
- field public static final int INVALID_PCSCF_ADDR = 113; // 0x71
- field public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 124; // 0x7c
- field public static final int INVALID_PRIMARY_NSAPI = 2158; // 0x86e
- field public static final int INVALID_SIM_STATE = 2224; // 0x8b0
- field public static final int INVALID_TRANSACTION_ID = 81; // 0x51
- field public static final int IPV6_ADDRESS_TRANSFER_FAILED = 2047; // 0x7ff
- field public static final int IPV6_PREFIX_UNAVAILABLE = 2250; // 0x8ca
- field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77
- field public static final int IP_VERSION_MISMATCH = 2055; // 0x807
- field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892
- field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829
- field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba
- field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb
- field public static final int LLC_SNDCP = 25; // 0x19
- field public static final int LOCAL_END = 2215; // 0x8a7
- field public static final int LOCATION_AREA_NOT_ALLOWED = 2102; // 0x836
- field public static final int LOST_CONNECTION = 65540; // 0x10004
- field public static final int LOWER_LAYER_REGISTRATION_FAILURE = 2197; // 0x895
- field public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 2044; // 0x7fc
- field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845
- field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f
- field public static final int MAC_FAILURE = 2183; // 0x887
- field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d
- field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876
- field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f
- field public static final int MAX_IPV4_CONNECTIONS = 2052; // 0x804
- field public static final int MAX_IPV6_CONNECTIONS = 2053; // 0x805
- field public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe
- field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f
- field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61
- field public static final int MIP_CONFIG_FAILURE = 2050; // 0x802
- field public static final int MIP_FA_ADMIN_PROHIBITED = 2001; // 0x7d1
- field public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 2012; // 0x7dc
- field public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 2008; // 0x7d8
- field public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 2004; // 0x7d4
- field public static final int MIP_FA_INSUFFICIENT_RESOURCES = 2002; // 0x7d2
- field public static final int MIP_FA_MALFORMED_REPLY = 2007; // 0x7d7
- field public static final int MIP_FA_MALFORMED_REQUEST = 2006; // 0x7d6
- field public static final int MIP_FA_MISSING_CHALLENGE = 2017; // 0x7e1
- field public static final int MIP_FA_MISSING_HOME_ADDRESS = 2015; // 0x7df
- field public static final int MIP_FA_MISSING_HOME_AGENT = 2014; // 0x7de
- field public static final int MIP_FA_MISSING_NAI = 2013; // 0x7dd
- field public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2003; // 0x7d3
- field public static final int MIP_FA_REASON_UNSPECIFIED = 2000; // 0x7d0
- field public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 2005; // 0x7d5
- field public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 2011; // 0x7db
- field public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 2010; // 0x7da
- field public static final int MIP_FA_STALE_CHALLENGE = 2018; // 0x7e2
- field public static final int MIP_FA_UNKNOWN_CHALLENGE = 2016; // 0x7e0
- field public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 2009; // 0x7d9
- field public static final int MIP_HA_ADMIN_PROHIBITED = 2020; // 0x7e4
- field public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 2029; // 0x7ed
- field public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 2023; // 0x7e7
- field public static final int MIP_HA_INSUFFICIENT_RESOURCES = 2021; // 0x7e5
- field public static final int MIP_HA_MALFORMED_REQUEST = 2025; // 0x7e9
- field public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2022; // 0x7e6
- field public static final int MIP_HA_REASON_UNSPECIFIED = 2019; // 0x7e3
- field public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 2024; // 0x7e8
- field public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 2028; // 0x7ec
- field public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 2027; // 0x7eb
- field public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 2026; // 0x7ea
- field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b
- field public static final int MODEM_APP_PREEMPTED = 2032; // 0x7f0
- field public static final int MODEM_RESTART = 2037; // 0x7f5
- field public static final int MSC_TEMPORARILY_NOT_REACHABLE = 2180; // 0x884
- field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65
- field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62
- field public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 2099; // 0x833
- field public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 2192; // 0x890
- field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37
- field public static final int NAS_LAYER_FAILURE = 2191; // 0x88f
- field public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 2167; // 0x877
- field public static final int NAS_SIGNALLING = 14; // 0xe
- field public static final int NETWORK_FAILURE = 38; // 0x26
- field public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 2154; // 0x86a
- field public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 2153; // 0x869
- field public static final int NETWORK_INITIATED_TERMINATION = 2031; // 0x7ef
- field public static final int NONE = 0; // 0x0
- field public static final int NON_IP_NOT_SUPPORTED = 2069; // 0x815
- field public static final int NORMAL_RELEASE = 2218; // 0x8aa
- field public static final int NO_CDMA_SERVICE = 2084; // 0x824
- field public static final int NO_COLLOCATED_HDR = 2225; // 0x8b1
- field public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 2189; // 0x88d
- field public static final int NO_GPRS_CONTEXT = 2094; // 0x82e
- field public static final int NO_HYBRID_HDR_SERVICE = 2209; // 0x8a1
- field public static final int NO_PDP_CONTEXT_ACTIVATED = 2107; // 0x83b
- field public static final int NO_RESPONSE_FROM_BASE_STATION = 2081; // 0x821
- field public static final int NO_SERVICE = 2216; // 0x8a8
- field public static final int NO_SERVICE_ON_GATEWAY = 2093; // 0x82d
- field public static final int NSAPI_IN_USE = 35; // 0x23
- field public static final int NULL_APN_DISALLOWED = 2061; // 0x80d
- field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001
- field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a
- field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b
- field public static final int OEM_DCFAILCAUSE_12 = 4108; // 0x100c
- field public static final int OEM_DCFAILCAUSE_13 = 4109; // 0x100d
- field public static final int OEM_DCFAILCAUSE_14 = 4110; // 0x100e
- field public static final int OEM_DCFAILCAUSE_15 = 4111; // 0x100f
- field public static final int OEM_DCFAILCAUSE_2 = 4098; // 0x1002
- field public static final int OEM_DCFAILCAUSE_3 = 4099; // 0x1003
- field public static final int OEM_DCFAILCAUSE_4 = 4100; // 0x1004
- field public static final int OEM_DCFAILCAUSE_5 = 4101; // 0x1005
- field public static final int OEM_DCFAILCAUSE_6 = 4102; // 0x1006
- field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007
- field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008
- field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009
- field public static final int ONLY_IPV4V6_ALLOWED = 57; // 0x39
- field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32
- field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33
- field public static final int ONLY_NON_IP_ALLOWED = 58; // 0x3a
- field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34
- field public static final int OPERATOR_BARRED = 8; // 0x8
- field public static final int OTASP_COMMIT_IN_PROGRESS = 2208; // 0x8a0
- field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36
- field public static final int PDN_INACTIVITY_TIMER_EXPIRED = 2051; // 0x803
- field public static final int PDN_IPV4_CALL_DISALLOWED = 2033; // 0x7f1
- field public static final int PDN_IPV4_CALL_THROTTLED = 2034; // 0x7f2
- field public static final int PDN_IPV6_CALL_DISALLOWED = 2035; // 0x7f3
- field public static final int PDN_IPV6_CALL_THROTTLED = 2036; // 0x7f4
- field public static final int PDN_NON_IP_CALL_DISALLOWED = 2071; // 0x817
- field public static final int PDN_NON_IP_CALL_THROTTLED = 2070; // 0x816
- field public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 2109; // 0x83d
- field public static final int PDP_DUPLICATE = 2104; // 0x838
- field public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 2161; // 0x871
- field public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 2163; // 0x873
- field public static final int PDP_LOWERLAYER_ERROR = 2164; // 0x874
- field public static final int PDP_MODIFY_COLLISION = 2165; // 0x875
- field public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 2162; // 0x872
- field public static final int PDP_PPP_NOT_SUPPORTED = 2038; // 0x7f6
- field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e
- field public static final int PHONE_IN_USE = 2222; // 0x8ae
- field public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 2040; // 0x7f8
- field public static final int PLMN_NOT_ALLOWED = 2101; // 0x835
- field public static final int PPP_AUTH_FAILURE = 2229; // 0x8b5
- field public static final int PPP_CHAP_FAILURE = 2232; // 0x8b8
- field public static final int PPP_CLOSE_IN_PROGRESS = 2233; // 0x8b9
- field public static final int PPP_OPTION_MISMATCH = 2230; // 0x8b6
- field public static final int PPP_PAP_FAILURE = 2231; // 0x8b7
- field public static final int PPP_TIMEOUT = 2228; // 0x8b4
- field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc
- field public static final int PROFILE_BEARER_INCOMPATIBLE = 2042; // 0x7fa
- field public static final int PROTOCOL_ERRORS = 111; // 0x6f
- field public static final int QOS_NOT_ACCEPTED = 37; // 0x25
- field public static final int RADIO_ACCESS_BEARER_FAILURE = 2110; // 0x83e
- field public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 2160; // 0x870
- field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001
- field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb
- field public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 2220; // 0x8ac
- field public static final int REGISTRATION_FAIL = -1; // 0xffffffff
- field public static final int REGULAR_DEACTIVATION = 36; // 0x24
- field public static final int REJECTED_BY_BASE_STATION = 2082; // 0x822
- field public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 2173; // 0x87d
- field public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 2174; // 0x87e
- field public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 2171; // 0x87b
- field public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 2175; // 0x87f
- field public static final int RRC_CONNECTION_ABORT_REQUEST = 2151; // 0x867
- field public static final int RRC_CONNECTION_ACCESS_BARRED = 2139; // 0x85b
- field public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 2137; // 0x859
- field public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 2138; // 0x85a
- field public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 2144; // 0x860
- field public static final int RRC_CONNECTION_CELL_RESELECTION = 2140; // 0x85c
- field public static final int RRC_CONNECTION_CONFIG_FAILURE = 2141; // 0x85d
- field public static final int RRC_CONNECTION_INVALID_REQUEST = 2168; // 0x878
- field public static final int RRC_CONNECTION_LINK_FAILURE = 2143; // 0x85f
- field public static final int RRC_CONNECTION_NORMAL_RELEASE = 2147; // 0x863
- field public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 2150; // 0x866
- field public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 2148; // 0x864
- field public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 2149; // 0x865
- field public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 2146; // 0x862
- field public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 2172; // 0x87c
- field public static final int RRC_CONNECTION_RF_UNAVAILABLE = 2170; // 0x87a
- field public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 2152; // 0x868
- field public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 2145; // 0x861
- field public static final int RRC_CONNECTION_TIMER_EXPIRED = 2142; // 0x85e
- field public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 2169; // 0x879
- field public static final int RRC_UPLINK_CONNECTION_RELEASE = 2134; // 0x856
- field public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 2132; // 0x854
- field public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 2133; // 0x855
- field public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 2136; // 0x858
- field public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 2135; // 0x857
- field public static final int RUIM_NOT_PRESENT = 2085; // 0x825
- field public static final int SECURITY_MODE_REJECTED = 2186; // 0x88a
- field public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 2129; // 0x851
- field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21
- field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20
- field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
- field public static final int SIGNAL_LOST = -3; // 0xfffffffd
- field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb
- field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888
- field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894
- field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
- field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29
- field public static final int TFT_SYTAX_ERROR = 42; // 0x2a
- field public static final int THERMAL_EMERGENCY = 2090; // 0x82a
- field public static final int THERMAL_MITIGATION = 2062; // 0x80e
- field public static final int TRAT_SWAP_FAILED = 2048; // 0x800
- field public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 128; // 0x80
- field public static final int UE_IS_ENTERING_POWERSAVE_MODE = 2226; // 0x8b2
- field public static final int UE_RAT_CHANGE = 2105; // 0x839
- field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889
- field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897
- field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
- field public static final int UNACCEPTABLE_NETWORK_PARAMETER = 65538; // 0x10002
- field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b
- field public static final int UNKNOWN = 65536; // 0x10000
- field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
- field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c
- field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b
- field public static final int UNPREFERRED_RAT = 2039; // 0x7f7
- field public static final int UNSUPPORTED_1X_PREV = 2214; // 0x8a6
- field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42
- field public static final int UNSUPPORTED_QCI_VALUE = 59; // 0x3b
- field public static final int USER_AUTHENTICATION = 29; // 0x1d
- field public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 2245; // 0x8c5
- field public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be
- field public static final int VSNCP_GEN_ERROR = 2237; // 0x8bd
- field public static final int VSNCP_INSUFFICIENT_PARAMETERS = 2243; // 0x8c3
- field public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 2240; // 0x8c0
- field public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 2248; // 0x8c8
- field public static final int VSNCP_PDN_GATEWAY_REJECT = 2242; // 0x8c2
- field public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 2241; // 0x8c1
- field public static final int VSNCP_PDN_ID_IN_USE = 2246; // 0x8c6
- field public static final int VSNCP_PDN_LIMIT_EXCEEDED = 2239; // 0x8bf
- field public static final int VSNCP_RECONNECT_NOT_ALLOWED = 2249; // 0x8c9
- field public static final int VSNCP_RESOURCE_UNAVAILABLE = 2244; // 0x8c4
- field public static final int VSNCP_SUBSCRIBER_LIMITATION = 2247; // 0x8c7
- field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc
+ field @Deprecated public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be
}
public final class DataSpecificRegistrationInfo implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 957794c..9e37a3c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -846,6 +846,11 @@
method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
}
+ public static final class IntegrityFormula.SourceStamp {
+ method @NonNull public static android.content.integrity.IntegrityFormula notTrusted();
+ method @NonNull public static android.content.integrity.IntegrityFormula stampCertificateHashEquals(@NonNull String);
+ }
+
public final class Rule implements android.os.Parcelable {
ctor public Rule(@NonNull android.content.integrity.IntegrityFormula, int);
method public int describeContents();
@@ -1473,6 +1478,10 @@
method @NonNull public String getOriginalId();
}
+ public class MediaRouter2.RoutingController {
+ method @NonNull public String getOriginalId();
+ }
+
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -2529,6 +2538,11 @@
method public void log(android.os.StrictMode.ViolationInfo);
}
+ public static final class StrictMode.VmPolicy.Builder {
+ method @NonNull public android.os.StrictMode.VmPolicy.Builder detectIncorrectContextUse();
+ method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse();
+ }
+
public class SystemConfigManager {
method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
@@ -3145,6 +3159,7 @@
public abstract class InlineSuggestionRenderService extends android.app.Service {
ctor public InlineSuggestionRenderService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @Nullable public android.os.Bundle onGetInlineSuggestionsRendererInfo();
method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int);
field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService";
}
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index f56dd6e..95de6c5 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -129,7 +129,7 @@
}
std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
- const std::vector<uint8_t>& descriptor,
+ uint16_t bus, const std::vector<uint8_t>& descriptor,
std::unique_ptr<DeviceCallback> callback) {
size_t size = descriptor.size();
if (size > HID_MAX_DESCRIPTOR_SIZE) {
@@ -148,7 +148,7 @@
strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
ev.u.create2.rd_size = size;
- ev.u.create2.bus = BUS_BLUETOOTH;
+ ev.u.create2.bus = bus;
ev.u.create2.vendor = vid;
ev.u.create2.product = pid;
ev.u.create2.version = 0;
@@ -293,8 +293,8 @@
return data;
}
-static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid,
- jbyteArray rawDescriptor, jobject callback) {
+static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
+ jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
ScopedUtfChars name(env, rawName);
if (name.c_str() == nullptr) {
return 0;
@@ -305,7 +305,7 @@
std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
std::unique_ptr<uhid::Device> d =
- uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, desc,
+ uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc,
std::move(cb));
return reinterpret_cast<jlong>(d.release());
}
@@ -339,14 +339,14 @@
}
static JNINativeMethod sMethods[] = {
- { "nativeOpenDevice",
- "(Ljava/lang/String;III[B"
- "Lcom/android/commands/hid/Device$DeviceCallback;)J",
- reinterpret_cast<void*>(openDevice) },
- { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
- { "nativeSendGetFeatureReportReply", "(JI[B)V",
- reinterpret_cast<void*>(sendGetFeatureReportReply) },
- { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
+ {"nativeOpenDevice",
+ "(Ljava/lang/String;IIII[B"
+ "Lcom/android/commands/hid/Device$DeviceCallback;)J",
+ reinterpret_cast<void*>(openDevice)},
+ {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
+ {"nativeSendGetFeatureReportReply", "(JI[B)V",
+ reinterpret_cast<void*>(sendGetFeatureReportReply)},
+ {"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)},
};
int register_com_android_commands_hid_Device(JNIEnv* env) {
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.h b/cmds/hid/jni/com_android_commands_hid_Device.h
index 93ea881..7202b45 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.h
+++ b/cmds/hid/jni/com_android_commands_hid_Device.h
@@ -43,7 +43,7 @@
class Device {
public:
static std::unique_ptr<Device> open(int32_t id, const char* name, int32_t vid, int32_t pid,
- const std::vector<uint8_t>& descriptor,
+ uint16_t bus, const std::vector<uint8_t>& descriptor,
std::unique_ptr<DeviceCallback> callback);
~Device();
diff --git a/cmds/hid/src/com/android/commands/hid/Device.java b/cmds/hid/src/com/android/commands/hid/Device.java
index 874604c..dade415 100644
--- a/cmds/hid/src/com/android/commands/hid/Device.java
+++ b/cmds/hid/src/com/android/commands/hid/Device.java
@@ -52,13 +52,13 @@
System.loadLibrary("hidcommand_jni");
}
- private static native long nativeOpenDevice(String name, int id, int vid, int pid,
+ private static native long nativeOpenDevice(String name, int id, int vid, int pid, int bus,
byte[] descriptor, DeviceCallback callback);
private static native void nativeSendReport(long ptr, byte[] data);
private static native void nativeSendGetFeatureReportReply(long ptr, int id, byte[] data);
private static native void nativeCloseDevice(long ptr);
- public Device(int id, String name, int vid, int pid, byte[] descriptor,
+ public Device(int id, String name, int vid, int pid, int bus, byte[] descriptor,
byte[] report, SparseArray<byte[]> featureReports, Map<ByteBuffer, byte[]> outputs) {
mId = id;
mThread = new HandlerThread("HidDeviceHandler");
@@ -70,6 +70,7 @@
args.argi1 = id;
args.argi2 = vid;
args.argi3 = pid;
+ args.argi4 = bus;
if (name != null) {
args.arg1 = name;
} else {
@@ -115,7 +116,7 @@
case MSG_OPEN_DEVICE:
SomeArgs args = (SomeArgs) msg.obj;
mPtr = nativeOpenDevice((String) args.arg1, args.argi1, args.argi2, args.argi3,
- (byte[]) args.arg2, new DeviceCallback());
+ args.argi4, (byte[]) args.arg2, new DeviceCallback());
pauseEvents();
break;
case MSG_SEND_REPORT:
diff --git a/cmds/hid/src/com/android/commands/hid/Event.java b/cmds/hid/src/com/android/commands/hid/Event.java
index 62587a7..d4bf1d8 100644
--- a/cmds/hid/src/com/android/commands/hid/Event.java
+++ b/cmds/hid/src/com/android/commands/hid/Event.java
@@ -36,12 +36,28 @@
public static final String COMMAND_DELAY = "delay";
public static final String COMMAND_REPORT = "report";
+ // These constants come from "include/uapi/linux/input.h" in the kernel
+ enum Bus {
+ USB(0x03), BLUETOOTH(0x05);
+
+ Bus(int value) {
+ mValue = value;
+ }
+
+ int getValue() {
+ return mValue;
+ }
+
+ private int mValue;
+ }
+
private int mId;
private String mCommand;
private String mName;
private byte[] mDescriptor;
private int mVid;
private int mPid;
+ private Bus mBus;
private byte[] mReport;
private SparseArray<byte[]> mFeatureReports;
private Map<ByteBuffer, byte[]> mOutputs;
@@ -71,6 +87,10 @@
return mPid;
}
+ public int getBus() {
+ return mBus.getValue();
+ }
+
public byte[] getReport() {
return mReport;
}
@@ -94,6 +114,7 @@
+ ", descriptor=" + Arrays.toString(mDescriptor)
+ ", vid=" + mVid
+ ", pid=" + mPid
+ + ", bus=" + mBus
+ ", report=" + Arrays.toString(mReport)
+ ", feature_reports=" + mFeatureReports.toString()
+ ", outputs=" + mOutputs.toString()
@@ -144,6 +165,10 @@
mEvent.mPid = pid;
}
+ public void setBus(Bus bus) {
+ mEvent.mBus = bus;
+ }
+
public void setDuration(int duration) {
mEvent.mDuration = duration;
}
@@ -206,6 +231,9 @@
case "pid":
eb.setPid(readInt());
break;
+ case "bus":
+ eb.setBus(readBus());
+ break;
case "report":
eb.setReport(readData());
break;
@@ -264,6 +292,11 @@
return Integer.decode(val);
}
+ private Bus readBus() throws IOException {
+ String val = mReader.nextString();
+ return Bus.valueOf(val.toUpperCase());
+ }
+
private SparseArray<byte[]> readFeatureReports()
throws IllegalStateException, IOException {
SparseArray<byte[]> featureReports = new SparseArray<>();
diff --git a/cmds/hid/src/com/android/commands/hid/Hid.java b/cmds/hid/src/com/android/commands/hid/Hid.java
index 0ee2cc4..fac0ab2 100644
--- a/cmds/hid/src/com/android/commands/hid/Hid.java
+++ b/cmds/hid/src/com/android/commands/hid/Hid.java
@@ -113,7 +113,7 @@
"Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
}
int id = e.getId();
- Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(),
+ Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
e.getDescriptor(), e.getReport(), e.getFeatureReports(), e.getOutputs());
mDevices.append(id, d);
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 93522d4..73a8f66 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -217,7 +217,10 @@
shared_libs: ["libgtest_prod"],
- init_rc: ["statsd.rc"],
+ apex_available: [
+ "com.android.os.statsd",
+ "test_com.android.os.statsd",
+ ],
}
// ==============
@@ -299,6 +302,11 @@
static_libs: [
"libgmock",
"libplatformprotos",
+
+ // TODO(b/149842105): Make libstatssocket shared and remove libcutils once statsd_test is
+ // moved to the apex.
+ "libstatssocket",
+ "libcutils",
],
proto: {
@@ -308,7 +316,6 @@
shared_libs: [
"libprotobuf-cpp-lite",
- "libstatssocket"
],
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index a4e8fdc..43d0fce 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -392,6 +392,7 @@
WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"];
WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
+ SnapshotMergeReported snapshot_merge_reported = 255;
SdkExtensionStatus sdk_extension_status = 354;
}
@@ -4418,6 +4419,52 @@
optional int32 error_code = 2;
}
+/**
+ * Collects Virtual A/B statistics related to the use of dm-snapshot performed
+ * after an OTA.
+ *
+ * Logged from:
+ * - system/core/fs_mgr/libsnapshot/snapshot.cpp
+ * - system/core/fs_mgr/libsnapshot/snapshotctl.cpp
+ */
+message SnapshotMergeReported {
+ // Keep in sync with
+ // system/core/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+ enum UpdateState {
+ // No update or merge is in progress.
+ NONE = 0;
+ // An update is applying; snapshots may already exist.
+ INITIATED = 1;
+ // An update is pending, but has not been successfully booted yet.
+ UNVERIFIED = 2;
+ // The kernel is merging in the background.
+ MERGING = 3;
+ // Post-merge cleanup steps could not be completed due to a transient
+ // error, but the next reboot will finish any pending operations.
+ MERGE_NEEDS_REBOOT = 4;
+ // Merging is complete, and needs to be acknowledged.
+ MERGE_COMPLETED = 5;
+ // Merging failed due to an unrecoverable error.
+ MERGE_FAILED = 6;
+ // The update was implicitly cancelled, either by a rollback or a flash
+ // operation via fastboot. This state can only be returned by WaitForMerge.
+ CANCELLED = 7;
+ };
+
+ // Status of the update after the merge attempts.
+ optional UpdateState final_state = 1;
+
+ // Time to complete a merge operation in milliseconds.
+ // A negative value corresponds to the case in which the merge operation
+ // was interrupted and resumed (e.g. in case of a system reboot during the
+ // merge).
+ optional int64 duration_millis = 2;
+
+ // Number of reboots that occurred after issuing and before completing the
+ // merge of all the snapshot devices.
+ optional int32 intermediate_reboots = 3;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 4899b4a..dcfdfe3 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -75,11 +75,11 @@
(long long)id);
}
-static const char* findTrainInfoFileNameLocked(const string& trainName) {
+static string findTrainInfoFileNameLocked(const string& trainName) {
unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
if (dir == NULL) {
VLOG("Path %s does not exist", TRAIN_INFO_DIR);
- return nullptr;
+ return "";
}
dirent* de;
while ((de = readdir(dir.get()))) {
@@ -90,12 +90,12 @@
if (fileNameLength >= trainName.length()) {
if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
trainName.length())) {
- return fileName;
+ return string(fileName);
}
}
}
- return nullptr;
+ return "";
}
// Returns array of int64_t which contains timestamp in seconds, uid,
@@ -267,13 +267,13 @@
bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
- const char* fileName = findTrainInfoFileNameLocked(trainName);
- if (fileName == nullptr) {
+ string fileName = findTrainInfoFileNameLocked(trainName);
+ if (fileName.empty()) {
return false;
}
- int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName).c_str(), O_RDONLY | O_CLOEXEC);
+ int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName.c_str()).c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
- VLOG("Failed to open %s", fileName);
+ VLOG("Failed to open %s", fileName.c_str());
return false;
}
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 9707405..fe270a4 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,10 @@
* @param args The command-line arguments
*/
public static void main(String[] args) {
+ // Initialize the telephony module.
+ // TODO: Do it in zygote and RuntimeInit. b/148897549
+ ActivityThread.initializeMainlineModules();
+
(new Telecom()).run(args);
}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index c1e2195..3b0667d 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -27,6 +27,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -34,6 +35,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.Parcel;
@@ -786,12 +788,34 @@
* {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
* </p>
* @return The animated image resource id.
+ * @hide
*/
public int getAnimatedImageRes() {
return mAnimatedImageRes;
}
/**
+ * The animated image drawable.
+ * <p>
+ * <strong>Statically set from
+ * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ * @return The animated image drawable.
+ */
+ @Nullable
+ public Drawable loadAnimatedImage(@NonNull Context context) {
+ if (mAnimatedImageRes == /* invalid */ 0) {
+ return null;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = mComponentName.getPackageName();
+ final ApplicationInfo applicationInfo = mResolveInfo.serviceInfo.applicationInfo;
+
+ return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ }
+
+ /**
* Whether this service can retrieve the current window's content.
* <p>
* <strong>Statically set from
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 9912d2b..6209679 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -22,10 +22,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Xml;
@@ -193,12 +195,32 @@
* The animated image resource id of the accessibility shortcut target.
*
* @return The animated image resource id.
+ *
+ * @hide
*/
public int getAnimatedImageRes() {
return mAnimatedImageRes;
}
/**
+ * The animated image drawable of the accessibility shortcut target.
+ *
+ * @return The animated image drawable.
+ */
+ @Nullable
+ public Drawable loadAnimatedImage(@NonNull Context context) {
+ if (mAnimatedImageRes == /* invalid */ 0) {
+ return null;
+ }
+
+ final PackageManager packageManager = context.getPackageManager();
+ final String packageName = mComponentName.getPackageName();
+ final ApplicationInfo applicationInfo = mActivityInfo.applicationInfo;
+
+ return packageManager.getDrawable(packageName, mAnimatedImageRes, applicationInfo);
+ }
+
+ /**
* The localized html description of the accessibility shortcut target.
*
* @return The localized html description.
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
index 265674a..c6f42f7 100644
--- a/core/java/android/accessibilityservice/OWNERS
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -1,3 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
+qasid@google.com
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6b5bfda..8df26cb 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,9 @@
package android.app;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.StrictMode.vmIncorrectContextUseEnabled;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -64,6 +67,7 @@
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.StrictMode;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -247,6 +251,7 @@
private boolean mIsSystemOrSystemUiContext;
private boolean mIsUiContext;
+ private boolean mIsAssociatedWithDisplay;
@GuardedBy("mSync")
private File mDatabasesDir;
@@ -1891,9 +1896,20 @@
@Override
public Object getSystemService(String name) {
- if (isUiComponent(name) && !isUiContext()) {
- Log.w(TAG, name + " should be accessed from Activity or other visual Context");
+ // Check incorrect Context usage.
+ if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
+ final String errorMessage = "Tried to access visual service " + name
+ + " from a non-visual Context.";
+ final String message = "Visual services, such as WindowManager, WallpaperService or "
+ + "LayoutInflater should be accessed from Activity or other visual Context. "
+ + "Use an Activity or a Context created with "
+ + "Context#createWindowContext(int, Bundle), which are adjusted to the "
+ + "configuration and visual bounds of an area on screen.";
+ final Exception exception = new IllegalAccessException(errorMessage);
+ StrictMode.onIncorrectContextUsed(message, exception);
+ Log.e(TAG, errorMessage + message, exception);
}
+
return SystemServiceRegistry.getSystemService(this, name);
}
@@ -1902,8 +1918,17 @@
return SystemServiceRegistry.getSystemServiceName(serviceClass);
}
- boolean isUiContext() {
- return mIsSystemOrSystemUiContext || mIsUiContext;
+ private boolean isUiContext() {
+ return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI();
+ }
+
+ /**
+ * Temporary workaround to permit incorrect usages of Context by SystemUI.
+ * TODO(b/149790106): Fix usages and remove.
+ */
+ private boolean isSystemOrSystemUI() {
+ return ActivityThread.isSystem() || checkPermission("android.permission.STATUS_BAR_SERVICE",
+ Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED;
}
private static boolean isUiComponent(String name) {
@@ -1925,7 +1950,7 @@
final int appId = UserHandle.getAppId(uid);
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
- return PackageManager.PERMISSION_GRANTED;
+ return PERMISSION_GRANTED;
}
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+ permission);
@@ -1989,7 +2014,7 @@
private void enforce(
String permission, int resultOfCheck,
boolean selfToo, int uid, String message) {
- if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+ if (resultOfCheck != PERMISSION_GRANTED) {
throw new SecurityException(
(message != null ? (message + ": ") : "") +
(selfToo
@@ -2116,15 +2141,15 @@
if ((modeFlags&Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
if (readPermission == null
|| checkPermission(readPermission, pid, uid)
- == PackageManager.PERMISSION_GRANTED) {
- return PackageManager.PERMISSION_GRANTED;
+ == PERMISSION_GRANTED) {
+ return PERMISSION_GRANTED;
}
}
if ((modeFlags&Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
if (writePermission == null
|| checkPermission(writePermission, pid, uid)
- == PackageManager.PERMISSION_GRANTED) {
- return PackageManager.PERMISSION_GRANTED;
+ == PERMISSION_GRANTED) {
+ return PERMISSION_GRANTED;
}
}
return uri != null ? checkUriPermission(uri, pid, uid, modeFlags)
@@ -2157,7 +2182,7 @@
private void enforceForUri(
int modeFlags, int resultOfCheck, boolean selfToo,
int uid, Uri uri, String message) {
- if (resultOfCheck != PackageManager.PERMISSION_GRANTED) {
+ if (resultOfCheck != PERMISSION_GRANTED) {
throw new SecurityException(
(message != null ? (message + ": ") : "") +
(selfToo
@@ -2373,6 +2398,7 @@
null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
context.mDisplay = display;
+ context.mIsAssociatedWithDisplay = true;
return context;
}
@@ -2390,6 +2416,7 @@
ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mFeatureId,
mSplitName, token, mUser, mFlags, mClassLoader, null);
context.mIsUiContext = true;
+ context.mIsAssociatedWithDisplay = true;
return context;
}
@@ -2440,6 +2467,19 @@
@Override
public Display getDisplay() {
+ if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSystemOrSystemUI()) {
+ throw new UnsupportedOperationException("Tried to obtain display from a Context not "
+ + "associated with one. Only visual Contexts (such as Activity or one created "
+ + "with Context#createWindowContext) or ones created with "
+ + "Context#createDisplayContext are associated with displays. Other types of "
+ + "Contexts are typically related to background entities and may return an "
+ + "arbitrary display.");
+ }
+ return getDisplayNoVerify();
+ }
+
+ @Override
+ public Display getDisplayNoVerify() {
if (mDisplay == null) {
return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY,
mResources);
@@ -2450,13 +2490,14 @@
@Override
public int getDisplayId() {
- final Display display = getDisplay();
+ final Display display = getDisplayNoVerify();
return display != null ? display.getDisplayId() : Display.DEFAULT_DISPLAY;
}
@Override
public void updateDisplay(int displayId) {
mDisplay = mResourcesManager.getAdjustedDisplay(displayId, mResources);
+ mIsAssociatedWithDisplay = true;
}
@Override
@@ -2630,6 +2671,7 @@
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null,
activityInfo.splitName, activityToken, null, 0, classLoader, null);
context.mIsUiContext = true;
+ context.mIsAssociatedWithDisplay = true;
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 229bee5..5bc9992 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -28,9 +28,8 @@
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -87,50 +86,32 @@
}
@Override
- public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) {
- if (classLoadersChain.size() != classPaths.size()) {
- Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch");
- return;
- }
- if (classPaths.isEmpty()) {
- Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths");
- return;
- }
-
- // The first element of classPaths is the list of dex files that should be registered.
- // The classpath is represented as a list of dex files separated by File.pathSeparator.
- String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator);
- if (dexPathsForRegistration.length == 0) {
- // No dex files to register.
+ public void report(Map<String, String> classLoaderContextMap) {
+ if (classLoaderContextMap.isEmpty()) {
+ Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
return;
}
// Notify the package manager about the dex loads unconditionally.
// The load might be for either a primary or secondary dex file.
- notifyPackageManager(classLoadersChain, classPaths);
+ notifyPackageManager(classLoaderContextMap);
// Check for secondary dex files and register them for profiling if possible.
// Note that we only register the dex paths belonging to the first class loader.
- registerSecondaryDexForProfiling(dexPathsForRegistration);
+ registerSecondaryDexForProfiling(classLoaderContextMap.keySet());
}
- private void notifyPackageManager(List<ClassLoader> classLoadersChain,
- List<String> classPaths) {
+ private void notifyPackageManager(Map<String, String> classLoaderContextMap) {
// Get the class loader names for the binder call.
- List<String> classLoadersNames = new ArrayList<>(classPaths.size());
- for (ClassLoader classLoader : classLoadersChain) {
- classLoadersNames.add(classLoader.getClass().getName());
- }
String packageName = ActivityThread.currentPackageName();
try {
- ActivityThread.getPackageManager().notifyDexLoad(
- packageName, classLoadersNames, classPaths,
- VMRuntime.getRuntime().vmInstructionSet());
+ ActivityThread.getPackageManager().notifyDexLoad(packageName,
+ classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet());
} catch (RemoteException re) {
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
}
}
- private void registerSecondaryDexForProfiling(String[] dexPaths) {
+ private void registerSecondaryDexForProfiling(Set<String> dexPaths) {
if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
return;
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 4b0cadb..2122e92 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.PasswordMetrics;
import android.app.trust.ITrustManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -676,8 +677,9 @@
/**
* Determine if a given password is valid based off its lock type and expected complexity level.
*
- * @param isPin - whether this is a PIN-type password (only digits)
- * @param password - password to validate
+ * @param lockType - type of lock as specified in {@link LockTypes}
+ * @param password - password to validate; this has the same encoding
+ * as the output of String#getBytes
* @param complexity - complexity level imposed by the requester
* as defined in {@code DevicePolicyManager.PasswordComplexity}
* @return true if the password is valid, false otherwise
@@ -685,8 +687,8 @@
*/
@RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@SystemApi
- public boolean validateLockPasswordComplexity(
- boolean isPin, @NonNull byte[] password, int complexity) {
+ public boolean isValidLockPasswordComplexity(@LockTypes int lockType, @NonNull byte[] password,
+ @PasswordComplexity int complexity) {
if (!checkInitialLockMethodUsage()) {
return false;
}
@@ -696,9 +698,11 @@
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
PasswordMetrics adminMetrics =
devicePolicyManager.getPasswordMinimumMetrics(mContext.getUserId());
+ // Check if the password fits the mold of a pin or pattern.
+ boolean isPinOrPattern = lockType != LockTypes.PASSWORD;
return PasswordMetrics.validatePassword(
- adminMetrics, complexity, isPin, password).size() == 0;
+ adminMetrics, complexity, isPinOrPattern, password).size() == 0;
}
/**
@@ -712,7 +716,7 @@
*/
@RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@SystemApi
- public int getMinLockLength(boolean isPin, int complexity) {
+ public int getMinLockLength(boolean isPin, @PasswordComplexity int complexity) {
if (!checkInitialLockMethodUsage()) {
return -1;
}
@@ -731,7 +735,8 @@
* Set the lockscreen password after validating against its expected complexity level.
*
* @param lockType - type of lock as specified in {@link LockTypes}
- * @param password - password to validate
+ * @param password - password to validate; this has the same encoding
+ * as the output of String#getBytes
* @param complexity - complexity level imposed by the requester
* as defined in {@code DevicePolicyManager.PasswordComplexity}
* @return true if the lock is successfully set, false otherwise
@@ -739,7 +744,8 @@
*/
@RequiresPermission(Manifest.permission.SET_INITIAL_LOCK)
@SystemApi
- public boolean setLock(@LockTypes int lockType, @NonNull byte[] password, int complexity) {
+ public boolean setLock(@LockTypes int lockType, @NonNull byte[] password,
+ @PasswordComplexity int complexity) {
if (!checkInitialLockMethodUsage()) {
return false;
}
@@ -750,7 +756,7 @@
Log.e(TAG, "Password already set, rejecting call to setLock");
return false;
}
- if (!validateLockPasswordComplexity(lockType != LockTypes.PASSWORD, password, complexity)) {
+ if (!isValidLockPasswordComplexity(lockType, password, complexity)) {
Log.e(TAG, "Password is not valid, rejecting call to setLock");
return false;
}
diff --git a/core/java/android/app/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java
index 761b225..5ebcc46 100644
--- a/core/java/android/app/TaskEmbedder.java
+++ b/core/java/android/app/TaskEmbedder.java
@@ -597,7 +597,7 @@
if (mTmpDisplayMetrics == null) {
mTmpDisplayMetrics = new DisplayMetrics();
}
- mContext.getDisplay().getMetrics(mTmpDisplayMetrics);
+ mContext.getDisplayNoVerify().getRealMetrics(mTmpDisplayMetrics);
return mTmpDisplayMetrics.densityDpi;
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 5f74d2e..d9405e1 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -2097,7 +2097,7 @@
public ColorManagementProxy(@NonNull Context context) {
// Get a list of supported wide gamut color spaces.
- Display display = context.getDisplay();
+ Display display = context.getDisplayNoVerify();
if (display != null) {
mSupportedColorSpaces.addAll(Arrays.asList(display.getSupportedWideColorGamut()));
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 139b179..b219394 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -33,6 +33,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.annotation.UserHandleAware;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -5740,6 +5741,25 @@
}
/**
+ * Returns whether the admin has enabled always-on VPN lockdown for the current user.
+ *
+ * Only callable by the system.
+ * @hide
+ */
+ @UserHandleAware
+ public boolean isAlwaysOnVpnLockdownEnabled() {
+ throwIfParentInstance("isAlwaysOnVpnLockdownEnabled");
+ if (mService != null) {
+ try {
+ return mService.isAlwaysOnVpnLockdownEnabledForUser(myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by device or profile owner to query the set of packages that are allowed to access
* the network directly when always-on VPN is in lockdown mode but not connected. Returns
* {@code null} when always-on VPN is not active or not in lockdown mode.
@@ -5786,6 +5806,26 @@
}
/**
+ * Returns the VPN package name if the admin has enabled always-on VPN on the current user,
+ * or {@code null} if none is set.
+ *
+ * Only callable by the system.
+ * @hide
+ */
+ @UserHandleAware
+ public @Nullable String getAlwaysOnVpnPackage() {
+ throwIfParentInstance("getAlwaysOnVpnPackage");
+ if (mService != null) {
+ try {
+ return mService.getAlwaysOnVpnPackageForUser(myUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by an application that is administering the device to disable all cameras on the
* device, for this user. After setting this, no applications running as this user will be able
* to access any cameras on the device.
@@ -8949,49 +8989,6 @@
}
/**
- * Called by device owners to request a location provider to change its allowed state. For a
- * provider to be enabled requires both that the master location setting is enabled, and that
- * the provider itself is allowed. Most location providers are always allowed. Some location
- * providers may have user consents or terms and conditions that must be accepted, or some other
- * type of blocker before they are allowed however. Every location provider is responsible for
- * its own allowed state.
- *
- * <p>This method requests that a location provider change its allowed state. For providers that
- * are always allowed and have no state to change, this will have no effect. If the provider
- * does require some consent, terms and conditions, or other blocking state, using this API
- * implies that the device owner is agreeing/disagreeing to any consents, terms and conditions,
- * etc, and the provider should make a best effort to adjust it's allowed state accordingly.
- *
- * <p>Location providers are generally only responsible for the current user, and callers must
- * assume that this method will only affect provider state for the current user. Callers are
- * responsible for tracking current user changes and re-updating provider state as necessary.
- *
- * <p>While providers are expected to make a best effort to honor this request, it is not a
- * given that all providers will support such a request. If a provider does change its state as
- * a result of this request, that may happen asynchronously after some delay. Test location
- * providers set through {@link android.location.LocationManager#addTestProvider} will respond
- * to this request to aide in testing.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with
- * @param provider A location provider as listed by
- * {@link android.location.LocationManager#getAllProviders()}
- * @param providerAllowed Whether the location provider is being requested to enable or disable
- * itself
- * @throws SecurityException if {@code admin} is not a device owner.
- */
- public void requestSetLocationProviderAllowed(@NonNull ComponentName admin,
- @NonNull String provider, boolean providerAllowed) {
- throwIfParentInstance("requestSetLocationProviderAllowed");
- if (mService != null) {
- try {
- mService.requestSetLocationProviderAllowed(admin, provider, providerAllowed);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
* Called by profile or device owners to update {@link android.provider.Settings.Secure}
* settings. Validation that the value of the setting is in the correct form for the setting
* type should be performed by the caller.
@@ -9935,20 +9932,27 @@
}
/**
- * Called by device owner to control the security logging feature.
+ * Called by device owner or a profile owner of an organization-owned managed profile to
+ * control the security logging feature.
*
* <p> Security logs contain various information intended for security auditing purposes.
- * See {@link SecurityEvent} for details.
+ * When security logging is enabled by a profile owner of
+ * an organization-owned managed profile, certain security logs are not visible (for example
+ * personal app launch events) or they will be redacted (for example, details of the physical
+ * volume mount events). Please see {@link SecurityEvent} for details.
*
* <p><strong>Note:</strong> The device owner won't be able to retrieve security logs if there
* are unaffiliated secondary users or profiles on the device, regardless of whether the
* feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for
* all users to become affiliated. Therefore it's recommended that affiliation ids are set for
- * new users as soon as possible after provisioning via {@link #setAffiliationIds}.
+ * new users as soon as possible after provisioning via {@link #setAffiliationIds}. Profile
+ * owner of organization-owned managed profile is not subject to this restriction since all
+ * privacy-sensitive events happening outside the managed profile would have been redacted
+ * already.
*
- * @param admin Which device owner this request is associated with.
+ * @param admin Which device admin this request is associated with.
* @param enabled whether security logging should be enabled or not.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not allowed to control security logging.
* @see #setAffiliationIds
* @see #retrieveSecurityLogs
*/
@@ -9962,14 +9966,14 @@
}
/**
- * Return whether security logging is enabled or not by the device owner.
+ * Return whether security logging is enabled or not by the admin.
*
- * <p>Can only be called by the device owner, otherwise a {@link SecurityException} will be
- * thrown.
+ * <p>Can only be called by the device owner or a profile owner of an organization-owned
+ * managed profile, otherwise a {@link SecurityException} will be thrown.
*
- * @param admin Which device owner this request is associated with.
+ * @param admin Which device admin this request is associated with.
* @return {@code true} if security logging is enabled by device owner, {@code false} otherwise.
- * @throws SecurityException if {@code admin} is not a device owner.
+ * @throws SecurityException if {@code admin} is not allowed to control security logging.
*/
public boolean isSecurityLoggingEnabled(@Nullable ComponentName admin) {
throwIfParentInstance("isSecurityLoggingEnabled");
@@ -9981,20 +9985,21 @@
}
/**
- * Called by device owner to retrieve all new security logging entries since the last call to
- * this API after device boots.
+ * Called by device owner or profile owner of an organization-owned managed profile to retrieve
+ * all new security logging entries since the last call to this API after device boots.
*
* <p> Access to the logs is rate limited and it will only return new logs after the device
* owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}.
*
- * <p>If there is any other user or profile on the device, it must be affiliated with the
- * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}.
+ * <p> When called by a device owner, if there is any other user or profile on the device,
+ * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown.
+ * See {@link #isAffiliatedUser}.
*
- * @param admin Which device owner this request is associated with.
+ * @param admin Which device admin this request is associated with.
* @return the new batch of security logs which is a list of {@link SecurityEvent},
* or {@code null} if rate limitation is exceeded or if logging is currently disabled.
- * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device.
+ * @throws SecurityException if {@code admin} is not allowed to access security logging,
+ * or there is at least one profile or secondary user that is not affiliated with the device.
* @see #isAffiliatedUser
* @see DeviceAdminReceiver#onSecurityLogsAvailable
*/
@@ -10127,21 +10132,23 @@
}
/**
- * Called by device owners to retrieve device logs from before the device's last reboot.
+ * Called by device owner or profile owner of an organization-owned managed profile to retrieve
+ * device logs from before the device's last reboot.
* <p>
* <strong> This API is not supported on all devices. Calling this API on unsupported devices
* will result in {@code null} being returned. The device logs are retrieved from a RAM region
* which is not guaranteed to be corruption-free during power cycles, as a result be cautious
* about data corruption when parsing. </strong>
*
- * <p>If there is any other user or profile on the device, it must be affiliated with the
- * device. Otherwise a {@link SecurityException} will be thrown. See {@link #isAffiliatedUser}.
+ * <p> When called by a device owner, if there is any other user or profile on the device,
+ * it must be affiliated with the device. Otherwise a {@link SecurityException} will be thrown.
+ * See {@link #isAffiliatedUser}.
*
- * @param admin Which device owner this request is associated with.
+ * @param admin Which device admin this request is associated with.
* @return Device logs from before the latest reboot of the system, or {@code null} if this API
* is not supported on the device.
- * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
- * profile or secondary user that is not affiliated with the device.
+ * @throws SecurityException if {@code admin} is not allowed to access security logging, or
+ * there is at least one profile or secondary user that is not affiliated with the device.
* @see #isAffiliatedUser
* @see #retrieveSecurityLogs
*/
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 25c1e12..da48663 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -196,7 +196,9 @@
boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownWhitelist);
String getAlwaysOnVpnPackage(in ComponentName who);
+ String getAlwaysOnVpnPackageForUser(int userHandle);
boolean isAlwaysOnVpnLockdownEnabled(in ComponentName who);
+ boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle);
List<String> getAlwaysOnVpnLockdownWhitelist(in ComponentName who);
void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
@@ -270,7 +272,6 @@
boolean hasLockdownAdminConfiguredNetworks(in ComponentName who);
void setLocationEnabled(in ComponentName who, boolean locationEnabled);
- void requestSetLocationProviderAllowed(in ComponentName who, in String provider, boolean providerAllowed);
boolean setTime(in ComponentName who, long millis);
boolean setTimeZone(in ComponentName who, String timeZone);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 91cf120..fb7f573 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -23,11 +23,13 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.util.EventLog.Event;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
@@ -104,7 +106,8 @@
/**
* Indicates that a shell command was issued over ADB via {@code adb shell <command>}
* The log entry contains a {@code String} payload containing the shell command, accessible
- * via {@link SecurityEvent#getData()}.
+ * via {@link SecurityEvent#getData()}. If security logging is enabled on organization-owned
+ * managed profile devices, the shell command will be redacted to an empty string.
*/
public static final int TAG_ADB_SHELL_CMD = SecurityLogTags.SECURITY_ADB_SHELL_COMMAND;
@@ -133,6 +136,8 @@
* <li> [3] app pid ({@code Integer})
* <li> [4] seinfo tag ({@code String})
* <li> [5] SHA-256 hash of the base APK in hexadecimal ({@code String})
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_APP_PROCESS_START = SecurityLogTags.SECURITY_APP_PROCESS_START;
@@ -205,7 +210,8 @@
* following information about the event, encapsulated in an {@link Object} array and
* accessible via {@link SecurityEvent#getData()}:
* <li> [0] mount point ({@code String})
- * <li> [1] volume label ({@code String}).
+ * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned
+ * managed profile devices.
*/
public static final int TAG_MEDIA_MOUNT = SecurityLogTags.SECURITY_MEDIA_MOUNTED;
@@ -214,7 +220,8 @@
* following information about the event, encapsulated in an {@link Object} array and
* accessible via {@link SecurityEvent#getData()}:
* <li> [0] mount point ({@code String})
- * <li> [1] volume label ({@code String}).
+ * <li> [1] volume label ({@code String}). Redacted to empty string on organization-owned
+ * managed profile devices.
*/
public static final int TAG_MEDIA_UNMOUNT = SecurityLogTags.SECURITY_MEDIA_UNMOUNTED;
@@ -340,6 +347,9 @@
* <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
* <li> [1] alias of the key ({@code String})
* <li> [2] requesting process uid ({@code Integer}).
+ *
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_KEY_GENERATED =
SecurityLogTags.SECURITY_KEY_GENERATED;
@@ -351,6 +361,9 @@
* <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
* <li> [1] alias of the key ({@code String})
* <li> [2] requesting process uid ({@code Integer}).
+ *
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_KEY_IMPORT = SecurityLogTags.SECURITY_KEY_IMPORTED;
@@ -361,6 +374,9 @@
* <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
* <li> [1] alias of the key ({@code String})
* <li> [2] requesting process uid ({@code Integer}).
+ *
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_KEY_DESTRUCTION = SecurityLogTags.SECURITY_KEY_DESTROYED;
@@ -370,6 +386,11 @@
* {@link Object} array and accessible via {@link SecurityEvent#getData()}:
* <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
* <li> [1] subject of the certificate ({@code String}).
+ * <li> [2] which user the certificate is installed for ({@code Integer}), only available from
+ * version {@link android.os.Build.VERSION_CODES#R}.
+ *
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_CERT_AUTHORITY_INSTALLED =
SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED;
@@ -380,6 +401,11 @@
* {@link Object} array and accessible via {@link SecurityEvent#getData()}:
* <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
* <li> [1] subject of the certificate ({@code String}).
+ * <li> [2] which user the certificate is removed from ({@code Integer}), only available from
+ * version {@link android.os.Build.VERSION_CODES#R}.
+ *
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_CERT_AUTHORITY_REMOVED =
SecurityLogTags.SECURITY_CERT_AUTHORITY_REMOVED;
@@ -422,6 +448,9 @@
* {@link SecurityEvent#getData()}:
* <li> [0] alias of the key ({@code String})
* <li> [1] owner application uid ({@code Integer}).
+ *
+ * If security logging is enabled on organization-owned managed profile devices, only events
+ * happening inside the managed profile will be visible.
*/
public static final int TAG_KEY_INTEGRITY_VIOLATION =
SecurityLogTags.SECURITY_KEY_INTEGRITY_VIOLATION;
@@ -535,6 +564,16 @@
return mEvent.getData();
}
+ /** @hide */
+ public int getIntegerData(int index) {
+ return (Integer) ((Object[]) mEvent.getData())[index];
+ }
+
+ /** @hide */
+ public String getStringData(int index) {
+ return (String) ((Object[]) mEvent.getData())[index];
+ }
+
/**
* @hide
*/
@@ -554,7 +593,7 @@
* Returns severity level for the event.
*/
public @SecurityLogLevel int getLogLevel() {
- switch (mEvent.getTag()) {
+ switch (getTag()) {
case TAG_ADB_SHELL_INTERACTIVE:
case TAG_ADB_SHELL_CMD:
case TAG_SYNC_RECV_FILE:
@@ -608,6 +647,75 @@
return array.length >= 1 && array[0] instanceof Integer && (Integer) array[0] != 0;
}
+ /**
+ * Returns a copy of the security event suitable to be consumed by the provided user.
+ * This method will either return the original event itself if the event does not contain
+ * any sensitive data; or a copy of itself but with sensitive information redacted; or
+ * {@code null} if the entire event should not be accessed by the given user.
+ *
+ * @param accessingUser which user this security event is to be accessed, must be a
+ * concrete user id.
+ * @hide
+ */
+ public SecurityEvent redact(int accessingUser) {
+ // Which user the event is associated with, for the purpose of log redaction.
+ final int userId;
+ switch (getTag()) {
+ case SecurityLog.TAG_ADB_SHELL_CMD:
+ return new SecurityEvent(getId(), mEvent.withNewData("").getBytes());
+ case SecurityLog.TAG_MEDIA_MOUNT:
+ case SecurityLog.TAG_MEDIA_UNMOUNT:
+ // Partial redaction
+ String mountPoint;
+ try {
+ mountPoint = getStringData(0);
+ } catch (Exception e) {
+ return null;
+ }
+ return new SecurityEvent(getId(),
+ mEvent.withNewData(new Object[] {mountPoint, ""}).getBytes());
+ case SecurityLog.TAG_APP_PROCESS_START:
+ try {
+ userId = UserHandle.getUserId(getIntegerData(2));
+ } catch (Exception e) {
+ return null;
+ }
+ break;
+ case SecurityLog.TAG_CERT_AUTHORITY_INSTALLED:
+ case SecurityLog.TAG_CERT_AUTHORITY_REMOVED:
+ try {
+ userId = getIntegerData(2);
+ } catch (Exception e) {
+ return null;
+ }
+ break;
+ case SecurityLog.TAG_KEY_GENERATED:
+ case SecurityLog.TAG_KEY_IMPORT:
+ case SecurityLog.TAG_KEY_DESTRUCTION:
+ try {
+ userId = UserHandle.getUserId(getIntegerData(2));
+ } catch (Exception e) {
+ return null;
+ }
+ break;
+ case SecurityLog.TAG_KEY_INTEGRITY_VIOLATION:
+ try {
+ userId = UserHandle.getUserId(getIntegerData(1));
+ } catch (Exception e) {
+ return null;
+ }
+ break;
+ default:
+ userId = UserHandle.USER_NULL;
+ }
+ // If the event is not user-specific, or matches the accessing user, return it
+ // unmodified, else redact by returning null
+ if (userId == UserHandle.USER_NULL || accessingUser == userId) {
+ return this;
+ } else {
+ return null;
+ }
+ }
@Override
public int describeContents() {
@@ -657,6 +765,30 @@
return other != null && mEvent.equals(other.mEvent);
}
}
+
+ /**
+ * Redacts events in-place according to which user will consume the events.
+ *
+ * @param accessingUser which user will consume the redacted events, or UserHandle.USER_ALL if
+ * redaction should be skipped.
+ * @hide
+ */
+ public static void redactEvents(ArrayList<SecurityEvent> logList, int accessingUser) {
+ if (accessingUser == UserHandle.USER_ALL) return;
+ int end = 0;
+ for (int i = 0; i < logList.size(); i++) {
+ SecurityEvent event = logList.get(i);
+ event = event.redact(accessingUser);
+ if (event != null) {
+ logList.set(end, event);
+ end++;
+ }
+ }
+ for (int i = logList.size() - 1; i >= end; i--) {
+ logList.remove(i);
+ }
+ }
+
/**
* Retrieve all security logs and return immediately.
* @hide
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index 4e67fe2..100fd4c 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -33,8 +33,8 @@
210026 security_key_destroyed (success|1),(key_id|3),(uid|1)
210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3)
210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3)
-210029 security_cert_authority_installed (success|1),(subject|3)
-210030 security_cert_authority_removed (success|1),(subject|3)
+210029 security_cert_authority_installed (success|1),(subject|3),(target_user|1)
+210030 security_cert_authority_removed (success|1),(subject|3),(target_user|1)
210031 security_crypto_self_test_completed (success|1)
210032 security_key_integrity_violation (key_id|3),(uid|1)
210033 security_cert_validation_failure (reason|3)
diff --git a/core/java/android/app/trust/IStrongAuthTracker.aidl b/core/java/android/app/trust/IStrongAuthTracker.aidl
index 36c71bf..6d54396 100644
--- a/core/java/android/app/trust/IStrongAuthTracker.aidl
+++ b/core/java/android/app/trust/IStrongAuthTracker.aidl
@@ -23,4 +23,5 @@
*/
oneway interface IStrongAuthTracker {
void onStrongAuthRequiredChanged(int strongAuthRequired, int userId);
+ void onIsNonStrongBiometricAllowedChanged(boolean allowed, int userId);
}
\ No newline at end of file
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e1942da..bd3298c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -580,6 +580,15 @@
}
@Override
+ public void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+ RemoteCallback callback) {
+ final Bundle result = new Bundle();
+ result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ canonicalize(callingPkg, featureId, uri));
+ callback.sendResult(result);
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, String featureId, Uri uri) {
uri = validateIncomingUri(uri);
int userId = getUserIdFromUri(uri);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 0f1442d..7bc5901 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -359,6 +359,16 @@
return true;
}
+ case CANONICALIZE_ASYNC_TRANSACTION: {
+ data.enforceInterface(IContentProvider.descriptor);
+ String callingPkg = data.readString();
+ String featureId = data.readString();
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ RemoteCallback callback = RemoteCallback.CREATOR.createFromParcel(data);
+ canonicalizeAsync(callingPkg, featureId, uri, callback);
+ return true;
+ }
+
case UNCANONICALIZE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -823,6 +833,25 @@
}
@Override
+ /* oneway */ public void canonicalizeAsync(String callingPkg, @Nullable String featureId,
+ Uri uri, RemoteCallback callback) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ try {
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ data.writeString(callingPkg);
+ data.writeString(featureId);
+ uri.writeToParcel(data, 0);
+ callback.writeToParcel(data, 0);
+
+ mRemote.transact(IContentProvider.CANONICALIZE_ASYNC_TRANSACTION, data, null,
+ Binder.FLAG_ONEWAY);
+ } finally {
+ data.recycle();
+ }
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri url)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0e0161f..b748cfa 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -712,14 +712,17 @@
* {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
* @hide
*/
- public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
+ public static final int CONTENT_PROVIDER_READY_TIMEOUT_MILLIS =
CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
+ // Timeout given a ContentProvider that has already been started and connected to.
+ private static final int CONTENT_PROVIDER_TIMEOUT_MILLIS = 3 * 1000;
+
// Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
// long ActivityManagerService is giving a content provider to get published if a new process
// needs to be started for that.
- private static final int GET_TYPE_TIMEOUT_MILLIS =
- CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
+ private static final int REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS =
+ CONTENT_PROVIDER_READY_TIMEOUT_MILLIS + CONTENT_PROVIDER_TIMEOUT_MILLIS;
public ContentResolver(@Nullable Context context) {
this(context, null);
@@ -833,10 +836,10 @@
IContentProvider provider = acquireExistingProvider(url);
if (provider != null) {
try {
- final GetTypeResultListener resultListener = new GetTypeResultListener();
+ final StringResultListener resultListener = new StringResultListener();
provider.getTypeAsync(url, new RemoteCallback(resultListener));
- resultListener.waitForResult();
- return resultListener.type;
+ resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ return resultListener.result;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
@@ -854,13 +857,13 @@
}
try {
- GetTypeResultListener resultListener = new GetTypeResultListener();
+ final StringResultListener resultListener = new StringResultListener();
ActivityManager.getService().getProviderMimeTypeAsync(
ContentProvider.getUriWithoutUserId(url),
resolveUserId(url),
new RemoteCallback(resultListener));
- resultListener.waitForResult();
- return resultListener.type;
+ resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ return resultListener.result;
} catch (RemoteException e) {
// We just failed to send a oneway request to the System Server. Nothing to do.
return null;
@@ -870,27 +873,29 @@
}
}
- private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
+ private abstract static class ResultListener<T> implements RemoteCallback.OnResultListener {
@GuardedBy("this")
public boolean done;
@GuardedBy("this")
- public String type;
+ public T result;
@Override
public void onResult(Bundle result) {
synchronized (this) {
- type = result.getString(REMOTE_CALLBACK_RESULT);
+ this.result = getResultFromBundle(result);
done = true;
notifyAll();
}
}
- public void waitForResult() {
+ protected abstract T getResultFromBundle(Bundle result);
+
+ public void waitForResult(long timeout) {
synchronized (this) {
if (!done) {
try {
- wait(GET_TYPE_TIMEOUT_MILLIS);
+ wait(timeout);
} catch (InterruptedException e) {
// Ignore
}
@@ -899,6 +904,20 @@
}
}
+ private static class StringResultListener extends ResultListener<String> {
+ @Override
+ protected String getResultFromBundle(Bundle result) {
+ return result.getString(REMOTE_CALLBACK_RESULT);
+ }
+ }
+
+ private static class UriResultListener extends ResultListener<Uri> {
+ @Override
+ protected Uri getResultFromBundle(Bundle result) {
+ return result.getParcelable(REMOTE_CALLBACK_RESULT);
+ }
+ }
+
/**
* Query for the possible MIME types for the representations the given
* content URL can be returned when opened as as stream with
@@ -1192,7 +1211,11 @@
}
try {
- return provider.canonicalize(mPackageName, mFeatureId, url);
+ final UriResultListener resultListener = new UriResultListener();
+ provider.canonicalizeAsync(mPackageName, mFeatureId, url,
+ new RemoteCallback(resultListener));
+ resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+ return resultListener.result;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ae12de0..c6e84b7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3495,13 +3495,23 @@
* <dl>
* <dt> {@link #WINDOW_SERVICE} ("window")
* <dd> The top-level window manager in which you can place custom
- * windows. The returned object is a {@link android.view.WindowManager}.
+ * windows. The returned object is a {@link android.view.WindowManager}. Must only be obtained
+ * from a visual context such as Activity or a Context created with
+ * {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
+ * visual bounds of an area on screen.
* <dt> {@link #LAYOUT_INFLATER_SERVICE} ("layout_inflater")
* <dd> A {@link android.view.LayoutInflater} for inflating layout resources
- * in this context.
+ * in this context. Must only be obtained from a visual context such as Activity or a Context
+ * created with {@link #createWindowContext(int, Bundle)}, which are adjusted to the
+ * configuration and visual bounds of an area on screen.
* <dt> {@link #ACTIVITY_SERVICE} ("activity")
* <dd> A {@link android.app.ActivityManager} for interacting with the
* global activity state of the system.
+ * <dt> {@link #WALLPAPER_SERVICE} ("wallpaper")
+ * <dd> A {@link android.service.wallpaper.WallpaperService} for accessing wallpapers in this
+ * context. Must only be obtained from a visual context such as Activity or a Context created
+ * with {@link #createWindowContext(int, Bundle)}, which are adjusted to the configuration and
+ * visual bounds of an area on screen.
* <dt> {@link #POWER_SERVICE} ("power")
* <dd> A {@link android.os.PowerManager} for controlling power
* management.
@@ -5901,6 +5911,8 @@
* {@link #createDisplayContext(Display)} to get a display object associated with a Context, or
* {@link android.hardware.display.DisplayManager#getDisplay} to get a display object by id.
* @return Returns the {@link Display} object this context is associated with.
+ * @throws UnsupportedOperationException if the method is called on an instance that is not
+ * associated with any display.
*/
@Nullable
public Display getDisplay() {
@@ -5908,6 +5920,17 @@
}
/**
+ * A version of {@link #getDisplay()} that does not perform a Context misuse check to be used by
+ * legacy APIs.
+ * TODO(b/149790106): Fix usages and remove.
+ * @hide
+ */
+ @Nullable
+ public Display getDisplayNoVerify() {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Gets the ID of the display this context is associated with.
*
* @return display ID associated with this {@link Context}.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f6515e8..e5381ea 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1003,6 +1003,12 @@
return mBase.getDisplay();
}
+ /** @hide */
+ @Override
+ public @Nullable Display getDisplayNoVerify() {
+ return mBase.getDisplayNoVerify();
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 4658ba1..37643da 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -45,7 +45,7 @@
public String getType(Uri url) throws RemoteException;
/**
- * An oneway version of getType. The functionality is exactly the same, except that the
+ * A oneway version of getType. The functionality is exactly the same, except that the
* call returns immediately, and the resulting type is returned when available via
* a binder callback.
*/
@@ -126,6 +126,14 @@
public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException;
+ /**
+ * A oneway version of canonicalize. The functionality is exactly the same, except that the
+ * call returns immediately, and the resulting type is returned when available via
+ * a binder callback.
+ */
+ void canonicalizeAsync(String callingPkg, @Nullable String featureId, Uri uri,
+ RemoteCallback callback) throws RemoteException;
+
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException;
@@ -162,4 +170,5 @@
static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26;
static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27;
int GET_TYPE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 28;
+ int CANONICALIZE_ASYNC_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 29;
}
diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java
index 4be7e6d..4ec9476 100644
--- a/core/java/android/content/integrity/AppInstallMetadata.java
+++ b/core/java/android/content/integrity/AppInstallMetadata.java
@@ -42,6 +42,9 @@
private final List<String> mInstallerCertificates;
private final long mVersionCode;
private final boolean mIsPreInstalled;
+ private final boolean mIsStampTrusted;
+ // Raw string encoding for the SHA-256 hash of the certificate of the stamp.
+ private final String mStampCertificateHash;
private final Map<String, String> mAllowedInstallersAndCertificates;
private AppInstallMetadata(Builder builder) {
@@ -51,6 +54,8 @@
this.mInstallerCertificates = builder.mInstallerCertificates;
this.mVersionCode = builder.mVersionCode;
this.mIsPreInstalled = builder.mIsPreInstalled;
+ this.mIsStampTrusted = builder.mIsStampTrusted;
+ this.mStampCertificateHash = builder.mStampCertificateHash;
this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates;
}
@@ -84,9 +89,17 @@
return mIsPreInstalled;
}
- /**
- * Get the allowed installers and their corresponding cert.
- */
+ /** @see AppInstallMetadata.Builder#setIsStampTrusted(boolean) */
+ public boolean isStampTrusted() {
+ return mIsStampTrusted;
+ }
+
+ /** @see AppInstallMetadata.Builder#setStampCertificateHash(String) */
+ public String getStampCertificateHash() {
+ return mStampCertificateHash;
+ }
+
+ /** Get the allowed installers and their corresponding cert. */
public Map<String, String> getAllowedInstallersAndCertificates() {
return mAllowedInstallersAndCertificates;
}
@@ -95,13 +108,16 @@
public String toString() {
return String.format(
"AppInstallMetadata { PackageName = %s, AppCerts = %s, InstallerName = %s,"
- + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b }",
+ + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b, "
+ + "StampTrusted = %b, StampCert = %s }",
mPackageName,
mAppCertificates,
mInstallerName == null ? "null" : mInstallerName,
mInstallerCertificates == null ? "null" : mInstallerCertificates,
mVersionCode,
- mIsPreInstalled);
+ mIsPreInstalled,
+ mIsStampTrusted,
+ mStampCertificateHash == null ? "null" : mStampCertificateHash);
}
/** Builder class for constructing {@link AppInstallMetadata} objects. */
@@ -112,6 +128,8 @@
private List<String> mInstallerCertificates;
private long mVersionCode;
private boolean mIsPreInstalled;
+ private boolean mIsStampTrusted;
+ private String mStampCertificateHash;
private Map<String, String> mAllowedInstallersAndCertificates;
public Builder() {
@@ -203,6 +221,31 @@
}
/**
+ * Set certificate hash of the stamp embedded in the APK.
+ *
+ * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
+ * of the stamp.
+ *
+ * @see AppInstallMetadata#getStampCertificateHash()
+ */
+ @NonNull
+ public Builder setStampCertificateHash(@NonNull String stampCertificateHash) {
+ this.mStampCertificateHash = Objects.requireNonNull(stampCertificateHash);
+ return this;
+ }
+
+ /**
+ * Set whether the stamp embedded in the APK is trusted or not.
+ *
+ * @see AppInstallMetadata#isStampTrusted()
+ */
+ @NonNull
+ public Builder setIsStampTrusted(boolean isStampTrusted) {
+ this.mIsStampTrusted = isStampTrusted;
+ return this;
+ }
+
+ /**
* Build {@link AppInstallMetadata}.
*
* @throws IllegalArgumentException if package name or app certificate is null
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index d911eab..977a631 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -47,12 +47,14 @@
/** @hide */
@IntDef(
value = {
- PACKAGE_NAME,
- APP_CERTIFICATE,
- INSTALLER_NAME,
- INSTALLER_CERTIFICATE,
- VERSION_CODE,
- PRE_INSTALLED,
+ PACKAGE_NAME,
+ APP_CERTIFICATE,
+ INSTALLER_NAME,
+ INSTALLER_CERTIFICATE,
+ VERSION_CODE,
+ PRE_INSTALLED,
+ STAMP_TRUSTED,
+ STAMP_CERTIFICATE_HASH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Key {}
@@ -105,6 +107,20 @@
*/
public static final int PRE_INSTALLED = 5;
+ /**
+ * If the APK has an embedded trusted stamp.
+ *
+ * <p>Can only be used in {@link BooleanAtomicFormula}.
+ */
+ public static final int STAMP_TRUSTED = 6;
+
+ /**
+ * SHA-256 of the certificate used to sign the stamp embedded in the APK.
+ *
+ * <p>Can only be used in {@link StringAtomicFormula}.
+ */
+ public static final int STAMP_CERTIFICATE_HASH = 7;
+
public static final int EQ = 0;
public static final int GT = 1;
public static final int GTE = 2;
@@ -266,9 +282,7 @@
}
private static boolean isValidOperator(int operator) {
- return operator == EQ
- || operator == GT
- || operator == GTE;
+ return operator == EQ || operator == GT || operator == GTE;
}
private static long getLongMetadataValue(AppInstallMetadata appInstallMetadata, int key) {
@@ -300,7 +314,8 @@
key == PACKAGE_NAME
|| key == APP_CERTIFICATE
|| key == INSTALLER_CERTIFICATE
- || key == INSTALLER_NAME,
+ || key == INSTALLER_NAME
+ || key == STAMP_CERTIFICATE_HASH,
String.format(
"Key %s cannot be used with StringAtomicFormula", keyToString(key)));
mValue = null;
@@ -321,7 +336,8 @@
key == PACKAGE_NAME
|| key == APP_CERTIFICATE
|| key == INSTALLER_CERTIFICATE
- || key == INSTALLER_NAME,
+ || key == INSTALLER_NAME
+ || key == STAMP_CERTIFICATE_HASH,
String.format(
"Key %s cannot be used with StringAtomicFormula", keyToString(key)));
mValue = value;
@@ -329,15 +345,14 @@
}
/**
- * Constructs a new {@link StringAtomicFormula} together with handling the necessary
- * hashing for the given key.
+ * Constructs a new {@link StringAtomicFormula} together with handling the necessary hashing
+ * for the given key.
*
- * <p> The value will be automatically hashed with SHA256 and the hex digest will be
- * computed when the key is PACKAGE_NAME or INSTALLER_NAME and the value is more than 32
- * characters.
+ * <p>The value will be automatically hashed with SHA256 and the hex digest will be computed
+ * when the key is PACKAGE_NAME or INSTALLER_NAME and the value is more than 32 characters.
*
- * <p> The APP_CERTIFICATES and INSTALLER_CERTIFICATES are always delivered in hashed
- * form. So the isHashedValue is set to true by default.
+ * <p>The APP_CERTIFICATES, INSTALLER_CERTIFICATES, and STAMP_CERTIFICATE_HASH are always
+ * delivered in hashed form. So the isHashedValue is set to true by default.
*
* @throws IllegalArgumentException if {@code key} cannot be used with string value.
*/
@@ -347,13 +362,15 @@
key == PACKAGE_NAME
|| key == APP_CERTIFICATE
|| key == INSTALLER_CERTIFICATE
- || key == INSTALLER_NAME,
+ || key == INSTALLER_NAME
+ || key == STAMP_CERTIFICATE_HASH,
String.format(
"Key %s cannot be used with StringAtomicFormula", keyToString(key)));
mValue = hashValue(key, value);
mIsHashedValue =
key == APP_CERTIFICATE
- || key == INSTALLER_CERTIFICATE
+ || key == INSTALLER_CERTIFICATE
+ || key == STAMP_CERTIFICATE_HASH
? true
: !mValue.equals(value);
}
@@ -460,6 +477,8 @@
return appInstallMetadata.getInstallerCertificates();
case AtomicFormula.INSTALLER_NAME:
return Collections.singletonList(appInstallMetadata.getInstallerName());
+ case AtomicFormula.STAMP_CERTIFICATE_HASH:
+ return Collections.singletonList(appInstallMetadata.getStampCertificateHash());
default:
throw new IllegalStateException(
"Unexpected key in StringAtomicFormula: " + key);
@@ -502,7 +521,7 @@
public BooleanAtomicFormula(@Key int key) {
super(key);
checkArgument(
- key == PRE_INSTALLED,
+ key == PRE_INSTALLED || key == STAMP_TRUSTED,
String.format(
"Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
mValue = null;
@@ -519,7 +538,7 @@
public BooleanAtomicFormula(@Key int key, boolean value) {
super(key);
checkArgument(
- key == PRE_INSTALLED,
+ key == PRE_INSTALLED || key == STAMP_TRUSTED,
String.format(
"Key %s cannot be used with BooleanAtomicFormula", keyToString(key)));
mValue = value;
@@ -615,6 +634,8 @@
switch (key) {
case AtomicFormula.PRE_INSTALLED:
return appInstallMetadata.isPreInstalled();
+ case AtomicFormula.STAMP_TRUSTED:
+ return appInstallMetadata.isStampTrusted();
default:
throw new IllegalStateException(
"Unexpected key in BooleanAtomicFormula: " + key);
@@ -640,6 +661,10 @@
return "INSTALLER_CERTIFICATE";
case PRE_INSTALLED:
return "PRE_INSTALLED";
+ case STAMP_TRUSTED:
+ return "STAMP_TRUSTED";
+ case STAMP_CERTIFICATE_HASH:
+ return "STAMP_CERTIFICATE_HASH";
default:
throw new IllegalArgumentException("Unknown key " + key);
}
@@ -664,6 +689,8 @@
|| key == VERSION_CODE
|| key == INSTALLER_NAME
|| key == INSTALLER_CERTIFICATE
- || key == PRE_INSTALLED;
+ || key == PRE_INSTALLED
+ || key == STAMP_TRUSTED
+ || key == STAMP_CERTIFICATE_HASH;
}
}
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
index c5e5c8a..fc177721 100644
--- a/core/java/android/content/integrity/IntegrityFormula.java
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -90,8 +90,7 @@
return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
}
- private Application() {
- }
+ private Application() {}
}
/** Factory class for creating integrity formulas based on installer. */
@@ -117,26 +116,45 @@
*/
@NonNull
public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) {
- return new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE,
- installerCertificate);
+ return new StringAtomicFormula(
+ AtomicFormula.INSTALLER_CERTIFICATE, installerCertificate);
}
- private Installer() {
+ private Installer() {}
+ }
+
+ /** Factory class for creating integrity formulas based on source stamp. */
+ public static final class SourceStamp {
+ /** Returns an integrity formula that checks the equality to a stamp certificate hash. */
+ @NonNull
+ public static IntegrityFormula stampCertificateHashEquals(
+ @NonNull String stampCertificateHash) {
+ return new StringAtomicFormula(
+ AtomicFormula.STAMP_CERTIFICATE_HASH, stampCertificateHash);
}
+
+ /**
+ * Returns an integrity formula that is valid when stamp embedded in the APK is NOT trusted.
+ */
+ @NonNull
+ public static IntegrityFormula notTrusted() {
+ return new BooleanAtomicFormula(AtomicFormula.STAMP_TRUSTED, /* value= */ false);
+ }
+
+ private SourceStamp() {}
}
/** @hide */
@IntDef(
value = {
- COMPOUND_FORMULA_TAG,
- STRING_ATOMIC_FORMULA_TAG,
- LONG_ATOMIC_FORMULA_TAG,
- BOOLEAN_ATOMIC_FORMULA_TAG,
- INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG
+ COMPOUND_FORMULA_TAG,
+ STRING_ATOMIC_FORMULA_TAG,
+ LONG_ATOMIC_FORMULA_TAG,
+ BOOLEAN_ATOMIC_FORMULA_TAG,
+ INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG
})
@Retention(RetentionPolicy.SOURCE)
- @interface Tag {
- }
+ @interface Tag {}
/** @hide */
public static final int COMPOUND_FORMULA_TAG = 0;
@@ -171,8 +189,8 @@
public abstract boolean isAppCertificateFormula();
/**
- * Returns true when the formula (or one of its atomic formulas) has installer package name
- * or installer certificate as key.
+ * Returns true when the formula (or one of its atomic formulas) has installer package name or
+ * installer certificate as key.
*
* @hide
*/
@@ -243,15 +261,12 @@
return new CompoundFormula(CompoundFormula.AND, Arrays.asList(formulae));
}
- /**
- * Returns a formula that evaluates to true when {@code formula} evaluates to false.
- */
+ /** Returns a formula that evaluates to true when {@code formula} evaluates to false. */
@NonNull
public static IntegrityFormula not(@NonNull IntegrityFormula formula) {
return new CompoundFormula(CompoundFormula.NOT, Arrays.asList(formula));
}
// Constructor is package private so it cannot be inherited outside of this package.
- IntegrityFormula() {
- }
+ IntegrityFormula() {}
}
diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java
new file mode 100644
index 0000000..1cbbdca
--- /dev/null
+++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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;
+
+/**
+ * Dummy class to maintain legacy behavior of including a class in core source to toggle
+ * whether or not a shared library is stripped at build time.
+ *
+ * @hide
+ */
+public class AndroidTestBaseUpdater {
+}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index b5f4f80..8a89840 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -95,9 +95,9 @@
void registerShortcutChangeCallback(String callingPackage, long changedSince,
String packageName, in List shortcutIds, in List<LocusId> locusIds,
- in ComponentName componentName, int flags, in IShortcutChangeCallback callback,
- int callbackId);
- void unregisterShortcutChangeCallback(String callingPackage, int callbackId);
+ in ComponentName componentName, int flags, in IShortcutChangeCallback callback);
+ void unregisterShortcutChangeCallback(String callingPackage,
+ in IShortcutChangeCallback callback);
void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b89901a..e9cdbf28 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -482,19 +482,12 @@
* Notify the package manager that a list of dex files have been loaded.
*
* @param loadingPackageName the name of the package who performs the load
- * @param classLoadersNames the names of the class loaders present in the loading chain. The
- * list encodes the class loader chain in the natural order. The first class loader has
- * the second one as its parent and so on. The dex files present in the class path of the
- * first class loader will be recorded in the usage file.
- * @param classPaths the class paths corresponding to the class loaders names from
- * {@param classLoadersNames}. The the first element corresponds to the first class loader
- * and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}, or null if the class loader's classpath is not known.
- * The dex files found in the first class path will be recorded in the usage file.
+ * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+ * the class loader context that was used to load them.
* @param loaderIsa the ISA of the loader process
*/
- oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
- in List<String> classPaths, String loaderIsa);
+ oneway void notifyDexLoad(String loadingPackageName,
+ in Map<String, String> classLoaderContextMap, String loaderIsa);
/**
* Register an application dex module with the package manager.
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 9e85fc3..29a55b7 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -76,4 +76,6 @@
void removeLongLivedShortcuts(String packageName, in List shortcutIds, int userId);
ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId);
+
+ void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
}
\ No newline at end of file
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 5aa0208..86242fd 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -17,6 +17,7 @@
package android.content.pm;
import static android.Manifest.permission;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -161,7 +162,7 @@
private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>();
private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>();
- private final Map<Integer, Pair<Executor, ShortcutChangeCallback>>
+ private final Map<ShortcutChangeCallback, Pair<Executor, IShortcutChangeCallback>>
mShortcutChangeCallbacks = new HashMap<>();
/**
@@ -549,8 +550,8 @@
android.content.pm.IShortcutChangeCallback.Stub {
private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences;
- ShortcutChangeCallbackProxy(Pair<Executor, ShortcutChangeCallback> remoteReferences) {
- mRemoteReferences = new WeakReference<>(remoteReferences);
+ ShortcutChangeCallbackProxy(Executor executor, ShortcutChangeCallback callback) {
+ mRemoteReferences = new WeakReference<>(new Pair<>(executor, callback));
}
@Override
@@ -1753,14 +1754,12 @@
Objects.requireNonNull(executor, "Executor cannot be null");
synchronized (mShortcutChangeCallbacks) {
- final int callbackId = callback.hashCode();
- final Pair<Executor, ShortcutChangeCallback> state = new Pair<>(executor, callback);
- mShortcutChangeCallbacks.put(callbackId, state);
+ IShortcutChangeCallback proxy = new ShortcutChangeCallbackProxy(executor, callback);
+ mShortcutChangeCallbacks.put(callback, new Pair<>(executor, proxy));
try {
mService.registerShortcutChangeCallback(mContext.getPackageName(),
query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds,
- query.mActivity, query.mQueryFlags, new ShortcutChangeCallbackProxy(state),
- callbackId);
+ query.mActivity, query.mQueryFlags, proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1779,12 +1778,10 @@
Objects.requireNonNull(callback, "Callback cannot be null");
synchronized (mShortcutChangeCallbacks) {
- final int callbackId = callback.hashCode();
- if (mShortcutChangeCallbacks.containsKey(callbackId)) {
- mShortcutChangeCallbacks.remove(callbackId);
+ if (mShortcutChangeCallbacks.containsKey(callback)) {
+ IShortcutChangeCallback proxy = mShortcutChangeCallbacks.remove(callback).second;
try {
- mService.unregisterShortcutChangeCallback(mContext.getPackageName(),
- callbackId);
+ mService.unregisterShortcutChangeCallback(mContext.getPackageName(), proxy);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 168679e..1de82450 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -41,7 +41,6 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-import android.annotation.AnyThread;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -56,12 +55,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageParserCacheHelper.ReadHelper;
-import android.content.pm.PackageParserCacheHelper.WriteHelper;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ApkParseUtils;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
@@ -79,14 +73,10 @@
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-import android.system.StructStat;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -118,7 +108,6 @@
import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -316,9 +305,6 @@
public int mParseError = PackageManager.INSTALL_SUCCEEDED;
- public ThreadLocal<ApkParseUtils.ParseResult> mSharedResult
- = ThreadLocal.withInitial(ApkParseUtils.ParseResult::new);
-
public static boolean sCompatibilityModeEnabled = true;
public static boolean sUseRoundIcon = false;
@@ -1054,7 +1040,7 @@
* and unique split names.
* <p>
* Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
+ * must be done separately in {@link #collectCertificates(Package, boolean)}.
*
* If {@code useCaches} is true, the package parser might return a cached
* result from a previous parse of the same {@code packageFile} with the same
@@ -1082,201 +1068,6 @@
}
/**
- * Updated method which returns {@link ParsedPackage}, the current representation of a
- * package parsed from disk.
- *
- * @see #parsePackage(File, int, boolean)
- */
- @AnyThread
- public ParsedPackage parseParsedPackage(File packageFile, int flags, boolean useCaches)
- throws PackageParserException {
- ParsedPackage parsed = useCaches ? getCachedResult(packageFile, flags) : null;
- if (parsed != null) {
- return parsed;
- }
-
- long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
- ApkParseUtils.ParseInput parseInput = mSharedResult.get().reset();
- parsed = ApkParseUtils.parsePackage(
- parseInput,
- mSeparateProcesses,
- mCallback,
- mMetrics,
- mOnlyCoreApps,
- packageFile,
- flags
- )
- .hideAsParsed();
-
- long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
- cacheResult(packageFile, flags, parsed);
- if (LOG_PARSE_TIMINGS) {
- parseTime = cacheTime - parseTime;
- cacheTime = SystemClock.uptimeMillis() - cacheTime;
- if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
- Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
- + "ms, update_cache=" + cacheTime + " ms");
- }
- }
-
- return parsed;
- }
-
- /**
- * Returns the cache key for a specified {@code packageFile} and {@code flags}.
- */
- private String getCacheKey(File packageFile, int flags) {
- StringBuilder sb = new StringBuilder(packageFile.getName());
- sb.append('-');
- sb.append(flags);
-
- return sb.toString();
- }
-
- @VisibleForTesting
- protected ParsedPackage fromCacheEntry(byte[] bytes) {
- return fromCacheEntryStatic(bytes);
- }
-
- /** static version of {@link #fromCacheEntry} for unit tests. */
- @VisibleForTesting
- public static ParsedPackage fromCacheEntryStatic(byte[] bytes) {
- final Parcel p = Parcel.obtain();
- p.unmarshall(bytes, 0, bytes.length);
- p.setDataPosition(0);
-
- final ReadHelper helper = new ReadHelper(p);
- helper.startAndInstall();
-
- // TODO(b/135203078): Hide PackageImpl constructor?
- ParsedPackage pkg = new PackageImpl(p);
-
- p.recycle();
-
- sCachedPackageReadCount.incrementAndGet();
-
- return pkg;
- }
-
- @VisibleForTesting
- protected byte[] toCacheEntry(ParsedPackage pkg) {
- return toCacheEntryStatic(pkg);
-
- }
-
- /** static version of {@link #toCacheEntry} for unit tests. */
- @VisibleForTesting
- public static byte[] toCacheEntryStatic(ParsedPackage pkg) {
- final Parcel p = Parcel.obtain();
- final WriteHelper helper = new WriteHelper(p);
-
- pkg.writeToParcel(p, 0 /* flags */);
-
- helper.finishAndUninstall();
-
- byte[] serialized = p.marshall();
- p.recycle();
-
- return serialized;
- }
-
- /**
- * Given a {@code packageFile} and a {@code cacheFile} returns whether the
- * cache file is up to date based on the mod-time of both files.
- */
- private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
- try {
- // NOTE: We don't use the File.lastModified API because it has the very
- // non-ideal failure mode of returning 0 with no excepions thrown.
- // The nio2 Files API is a little better but is considerably more expensive.
- final StructStat pkg = android.system.Os.stat(packageFile.getAbsolutePath());
- final StructStat cache = android.system.Os.stat(cacheFile.getAbsolutePath());
- return pkg.st_mtime < cache.st_mtime;
- } catch (ErrnoException ee) {
- // The most common reason why stat fails is that a given cache file doesn't
- // exist. We ignore that here. It's easy to reason that it's safe to say the
- // cache isn't up to date if we see any sort of exception here.
- //
- // (1) Exception while stating the package file : This should never happen,
- // and if it does, we do a full package parse (which is likely to throw the
- // same exception).
- // (2) Exception while stating the cache file : If the file doesn't exist, the
- // cache is obviously out of date. If the file *does* exist, we can't read it.
- // We will attempt to delete and recreate it after parsing the package.
- if (ee.errno != OsConstants.ENOENT) {
- Slog.w("Error while stating package cache : ", ee);
- }
-
- return false;
- }
- }
-
- /**
- * Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
- * or {@code null} if no cached result exists.
- */
- public ParsedPackage getCachedResult(File packageFile, int flags) {
- if (mCacheDir == null) {
- return null;
- }
-
- final String cacheKey = getCacheKey(packageFile, flags);
- final File cacheFile = new File(mCacheDir, cacheKey);
-
- try {
- // If the cache is not up to date, return null.
- if (!isCacheUpToDate(packageFile, cacheFile)) {
- return null;
- }
-
- final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
- return fromCacheEntry(bytes);
- } catch (Throwable e) {
- Slog.w(TAG, "Error reading package cache: ", e);
-
- // If something went wrong while reading the cache entry, delete the cache file
- // so that we regenerate it the next time.
- cacheFile.delete();
- return null;
- }
- }
-
- /**
- * Caches the parse result for {@code packageFile} with flags {@code flags}.
- */
- public void cacheResult(File packageFile, int flags, ParsedPackage parsed) {
- if (mCacheDir == null) {
- return;
- }
-
- try {
- final String cacheKey = getCacheKey(packageFile, flags);
- final File cacheFile = new File(mCacheDir, cacheKey);
-
- if (cacheFile.exists()) {
- if (!cacheFile.delete()) {
- Slog.e(TAG, "Unable to delete cache file: " + cacheFile);
- }
- }
-
- final byte[] cacheEntry = toCacheEntry(parsed);
-
- if (cacheEntry == null) {
- return;
- }
-
- try (FileOutputStream fos = new FileOutputStream(cacheFile)) {
- fos.write(cacheEntry);
- } catch (IOException ioe) {
- Slog.w(TAG, "Error writing cache entry.", ioe);
- cacheFile.delete();
- }
- } catch (Throwable e) {
- Slog.w(TAG, "Error saving package cache.", e);
- }
- }
-
- /**
* Parse all APKs contained in the given directory, treating them as a
* single package. This also performs sanity checking, such as requiring
* identical package name and version codes, a single base APK, and unique
@@ -1284,7 +1075,7 @@
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in
- * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
+ * {@link #collectCertificates(Package, boolean)} .
*/
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
@@ -1346,7 +1137,7 @@
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in
- * {@link ApkParseUtils#collectCertificates(AndroidPackage, boolean)}.
+ * {@link #collectCertificates(Package, boolean)}.
*/
@UnsupportedAppUsage
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
@@ -1695,7 +1486,7 @@
}
}
- private static String validateName(String name, boolean requireSeparator,
+ public static String validateName(String name, boolean requireSeparator,
boolean requireFilename) {
final int N = name.length();
boolean hasSep = false;
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index da7623a..30cf4e7 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -28,7 +28,7 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.BaseBundle;
import android.os.Debug;
import android.os.PersistableBundle;
@@ -163,7 +163,7 @@
}
public boolean isMatch(boolean isSystem, boolean isPackageEnabled,
- ComponentParseUtils.ParsedComponent component, int flags) {
+ ParsedMainComponent component, int flags) {
return isMatch(isSystem, isPackageEnabled, component.isEnabled(),
component.isDirectBootAware(), component.getName(), flags);
}
@@ -217,7 +217,7 @@
}
public boolean isEnabled(boolean isPackageEnabled,
- ComponentParseUtils.ParsedComponent parsedComponent, int flags) {
+ ParsedMainComponent parsedComponent, int flags) {
return isEnabled(isPackageEnabled, parsedComponent.isEnabled(), parsedComponent.getName(),
flags);
}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 55846ad..82b07f2 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -39,6 +39,8 @@
*/
public class ResolveInfo implements Parcelable {
private static final String TAG = "ResolveInfo";
+ private static final String INTENT_FORWARDER_ACTIVITY =
+ "com.android.internal.app.IntentForwarderActivity";
/**
* The activity or broadcast receiver that corresponds to this resolution
@@ -351,6 +353,16 @@
}
}
+ /**
+ * Returns whether this resolution represents the intent forwarder activity.
+ *
+ * @return whether this resolution represents the intent forwarder activity
+ */
+ public boolean isCrossProfileIntentForwarderActivity() {
+ return activityInfo != null
+ && INTENT_FORWARDER_ACTIVITY.equals(activityInfo.targetActivity);
+ }
+
public ResolveInfo() {
targetUserId = UserHandle.USER_CURRENT;
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 2863b26..fe4b36f 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,7 +20,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -39,28 +38,6 @@
public final class SharedLibraryInfo implements Parcelable {
/** @hide */
- public static SharedLibraryInfo createForStatic(AndroidPackage pkg) {
- return new SharedLibraryInfo(null, pkg.getPackageName(),
- pkg.makeListAllCodePaths(),
- pkg.getStaticSharedLibName(),
- pkg.getStaticSharedLibVersion(),
- TYPE_STATIC,
- new VersionedPackage(pkg.getManifestPackageName(),
- pkg.getLongVersionCode()),
- null, null);
- }
-
- /** @hide */
- public static SharedLibraryInfo createForDynamic(AndroidPackage pkg, String name) {
- return new SharedLibraryInfo(null, pkg.getPackageName(),
- pkg.makeListAllCodePaths(), name,
- (long) VERSION_UNDEFINED,
- TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(),
- pkg.getLongVersionCode()),
- null, null);
- }
-
- /** @hide */
@IntDef(flag = true, prefix = { "TYPE_" }, value = {
TYPE_BUILTIN,
TYPE_DYNAMIC,
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index bde4f61..49e8c05 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -1726,11 +1726,11 @@
}
/**
- * @return true if pinned but neither static nor dynamic.
+ * @return true if pinned or cached, but neither static nor dynamic.
* @hide
*/
public boolean isFloating() {
- return isPinned() && !(isDynamic() || isManifestShortcut());
+ return (isPinned() || isCached()) && !(isDynamic() || isManifestShortcut());
}
/** @hide */
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 3eea3f6..35c99a1 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -24,6 +24,7 @@
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
+import android.app.Notification;
import android.app.usage.UsageStatsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -741,4 +742,33 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Publish a single dynamic shortcut. If there are already dynamic or pinned shortcuts with the
+ * same ID, each mutable shortcut is updated.
+ *
+ * <p>This method is useful when posting notifications which are tagged with shortcut IDs; In
+ * order to make sure shortcuts exist and are up-to-date, without the need to explicitly handle
+ * the shortcut count limit.
+ * @see android.app.NotificationManager#notify(int, Notification)
+ * @see Notification.Builder#setShortcutId(String)
+ *
+ * <p>If {@link #getMaxShortcutCountPerActivity()} is already reached, an existing shortcut with
+ * the lowest rank will be removed to add space for the new shortcut.
+ *
+ * <p>If the rank of the shortcut is not explicitly set, it will be set to zero, and shortcut
+ * will be added to the top of the list.
+ *
+ * @throws IllegalArgumentException if trying to update an immutable shortcut.
+ *
+ * @throws IllegalStateException when the user is locked.
+ */
+ public void pushDynamicShortcut(@NonNull ShortcutInfo shortcut) {
+ try {
+ mService.pushDynamicShortcut(mContext.getPackageName(), shortcut, injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 3a93421..a50ce92 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -65,6 +65,9 @@
public abstract void addListener(@NonNull ShortcutChangeListener listener);
+ public abstract void addShortcutChangeCallback(
+ @NonNull LauncherApps.ShortcutChangeCallback callback);
+
public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 4cd201f..cdb1d22 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -22,7 +22,6 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.parsing.AndroidPackage;
import android.util.ArrayMap;
import android.util.jar.StrictJarFile;
@@ -87,16 +86,6 @@
*
* NOTE: involves I/O checks.
*/
- public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) {
- return buildPackageApkToDexMetadataMap(pkg.makeListAllCodePaths());
- }
-
- /**
- * Return the dex metadata files for the given package as a map
- * [code path -> dex metadata path].
- *
- * NOTE: involves I/O checks.
- */
private static Map<String, String> getPackageDexMetadata(PackageLite pkg) {
return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
}
@@ -114,7 +103,7 @@
* This should only be used for code paths extracted from a package structure after the naming
* was enforced in the installer.
*/
- private static Map<String, String> buildPackageApkToDexMetadataMap(
+ public static Map<String, String> buildPackageApkToDexMetadataMap(
List<String> codePaths) {
ArrayMap<String, String> result = new ArrayMap<>();
for (int i = codePaths.size() - 1; i >= 0; i--) {
@@ -157,25 +146,12 @@
}
/**
- * Validate the dex metadata files installed for the given package.
- *
- * @throws PackageParserException in case of errors.
- */
- public static void validatePackageDexMetadata(AndroidPackage pkg)
- throws PackageParserException {
- Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
- for (String dexMetadata : apkToDexMetadataList) {
- validateDexMetadataFile(dexMetadata);
- }
- }
-
- /**
* Validate that the given file is a dex metadata archive.
* This is just a sanity validation that the file is a zip archive.
*
* @throws PackageParserException if the file is not a .dm file.
*/
- private static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
+ public static void validateDexMetadataFile(String dmaPath) throws PackageParserException {
StrictJarFile jarFile = null;
try {
jarFile = new StrictJarFile(dmaPath, false, false);
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
deleted file mode 100644
index 2003ce2..0000000
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * Copyright (C) 2019 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.parsing;
-
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
-import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import java.security.PublicKey;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * The last state of a package during parsing/install before it is available in
- * {@link com.android.server.pm.PackageManagerService#mPackages}.
- *
- * It is the responsibility of the caller to understand what data is available at what step of the
- * parsing or install process.
- *
- * TODO(b/135203078): Nullability annotations
- * TODO(b/135203078): Remove get/setAppInfo differences
- *
- * @hide
- */
-public interface AndroidPackage extends Parcelable {
-
- /**
- * This will eventually be removed. Avoid calling this at all costs.
- */
- @Deprecated
- AndroidPackageWrite mutate();
-
- boolean canHaveOatDir();
-
- boolean cantSaveState();
-
- List<String> getAdoptPermissions();
-
- List<String> getAllCodePaths();
-
- List<String> getAllCodePathsExcludingResourceOnly();
-
- String getAppComponentFactory();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getClassLoaderName()}
- */
- @Deprecated
- String getAppInfoClassLoaderName();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getCodePath()}
- */
- @Deprecated
- String getAppInfoCodePath();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getName()}
- */
- @Deprecated
- String getAppInfoName();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getPackageName()}
- */
- @Deprecated
- String getAppInfoPackageName();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getProcessName()}
- */
- @Deprecated
- String getAppInfoProcessName();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getCodePath()}
- */
- @Deprecated
- String getAppInfoResourcePath();
-
- Bundle getAppMetaData();
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #getVolumeUuid()}
- */
- @Deprecated
- String getApplicationInfoVolumeUuid();
-
- String getBackupAgentName();
-
- int getBanner();
-
- String getBaseCodePath();
-
- int getBaseRevisionCode();
-
- int getCategory();
-
- String getClassLoaderName();
-
- String getClassName();
-
- String getCodePath();
-
- int getCompatibleWidthLimitDp();
-
- int getCompileSdkVersion();
-
- String getCompileSdkVersionCodeName();
-
- @Nullable
- List<ConfigurationInfo> getConfigPreferences();
-
- String getCpuAbiOverride();
-
- String getCredentialProtectedDataDir();
-
- String getDataDir();
-
- int getDescriptionRes();
-
- String getDeviceProtectedDataDir();
-
- List<FeatureGroupInfo> getFeatureGroups();
-
- int getFlags();
-
- int getFullBackupContent();
-
- int getHiddenApiEnforcementPolicy();
-
- int getIcon();
-
- int getIconRes();
-
- List<String> getImplicitPermissions();
-
- int getInstallLocation();
-
- Map<String, ArraySet<PublicKey>> getKeySetMapping();
-
- int getLabelRes();
-
- int getLargestWidthLimitDp();
-
- long[] getLastPackageUsageTimeInMills();
-
- long getLatestForegroundPackageUseTimeInMills();
-
- long getLatestPackageUseTimeInMills();
-
- List<String> getLibraryNames();
-
- int getLogo();
-
- long getLongVersionCode();
-
- String getManageSpaceActivityName();
-
- String getManifestPackageName();
-
- float getMaxAspectRatio();
-
- Bundle getMetaData(); // TODO(b/135203078): Make all the Bundles immutable
-
- @Nullable
- Set<String> getMimeGroups();
-
- float getMinAspectRatio();
-
- int getMinSdkVersion();
-
- String getName();
-
- String getNativeLibraryDir();
-
- String getNativeLibraryRootDir();
-
- int getNetworkSecurityConfigRes();
-
- CharSequence getNonLocalizedLabel();
-
- @Nullable
- List<String> getOriginalPackages();
-
- String getOverlayCategory();
-
- int getOverlayPriority();
-
- String getOverlayTarget();
-
- String getOverlayTargetName();
-
- /**
- * Map of overlayable name to actor name.
- */
- Map<String, String> getOverlayables();
-
- // TODO(b/135203078): Does this and getAppInfoPackageName have to be separate methods?
- // The refactor makes them the same value with no known consequences, so should be redundant.
- String getPackageName();
-
- @Nullable
- List<ParsedActivity> getActivities();
-
- @Nullable
- List<ParsedInstrumentation> getInstrumentations();
-
- @Nullable
- List<ParsedFeature> getFeatures();
-
- @Nullable
- List<ParsedPermissionGroup> getPermissionGroups();
-
- @Nullable
- List<ParsedPermission> getPermissions();
-
- @Nullable
- List<ParsedProvider> getProviders();
-
- @Nullable
- List<ParsedActivity> getReceivers();
-
- @Nullable
- List<ParsedService> getServices();
-
- String getPermission();
-
- @Nullable
- List<ParsedActivityIntentInfo> getPreferredActivityFilters();
-
- int getPreferredOrder();
-
- String getPrimaryCpuAbi();
-
- int getPrivateFlags();
-
- String getProcessName();
-
- @Nullable
- List<String> getProtectedBroadcasts();
-
- String getPublicSourceDir();
-
- List<Intent> getQueriesIntents();
-
- List<String> getQueriesPackages();
-
- Set<String> getQueriesProviders();
-
- String getRealPackage();
-
- // TODO(b/135203078): Rename to getRequiredFeatures? Somewhat ambiguous whether "Req" is
- // required or requested.
- @Nullable
- List<FeatureInfo> getReqFeatures();
-
- List<String> getRequestedPermissions();
-
- String getRequiredAccountType();
-
- int getRequiresSmallestWidthDp();
-
- byte[] getRestrictUpdateHash();
-
- String getRestrictedAccountType();
-
- int getRoundIconRes();
-
- String getScanPublicSourceDir();
-
- String getScanSourceDir();
-
- String getSeInfo();
-
- String getSeInfoUser();
-
- String getSecondaryCpuAbi();
-
- String getSecondaryNativeLibraryDir();
-
- String getSharedUserId();
-
- int getSharedUserLabel();
-
- PackageParser.SigningDetails getSigningDetails();
-
- String[] getSplitClassLoaderNames();
-
- @Nullable
- String[] getSplitCodePaths();
-
- @Nullable
- SparseArray<int[]> getSplitDependencies();
-
- int[] getSplitFlags();
-
- String[] getSplitNames();
-
- String[] getSplitPublicSourceDirs();
-
- int[] getSplitRevisionCodes();
-
- String getStaticSharedLibName();
-
- long getStaticSharedLibVersion();
-
- // TODO(b/135203078): Return String directly
- UUID getStorageUuid();
-
- int getTargetSandboxVersion();
-
- int getTargetSdkVersion();
-
- String getTaskAffinity();
-
- int getTheme();
-
- int getUiOptions();
-
- int getUid();
-
- Set<String> getUpgradeKeySets();
-
- @Nullable
- List<String> getUsesLibraries();
-
- @Nullable
- String[] getUsesLibraryFiles();
-
- List<SharedLibraryInfo> getUsesLibraryInfos();
-
- @Nullable
- List<String> getUsesOptionalLibraries();
-
- @Nullable
- List<String> getUsesStaticLibraries();
-
- @Nullable
- String[][] getUsesStaticLibrariesCertDigests();
-
- @Nullable
- long[] getUsesStaticLibrariesVersions();
-
- @Nullable
- ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses();
-
- int getVersionCode();
-
- int getVersionCodeMajor();
-
- String getVersionName();
-
- String getVolumeUuid();
-
- String getZygotePreloadName();
-
- boolean hasComponentClassName(String className);
-
- boolean hasPreserveLegacyExternalStorage();
-
- // App Info
-
- boolean hasRequestedLegacyExternalStorage();
-
- boolean isBaseHardwareAccelerated();
-
- boolean isCoreApp();
-
- boolean isDefaultToDeviceProtectedStorage();
-
- boolean isDirectBootAware();
-
- boolean isEmbeddedDexUsed();
-
- boolean isEnabled();
-
- boolean isCrossProfile();
-
- boolean isEncryptionAware();
-
- boolean isExternal();
-
- boolean isForceQueryable();
-
- boolean isForwardLocked();
-
- boolean isHiddenUntilInstalled();
-
- boolean isInstantApp();
-
- boolean isInternal();
-
- boolean isLibrary();
-
- // TODO(b/135203078): Should probably be in a utility class
- boolean isMatch(int flags);
-
- boolean isNativeLibraryRootRequiresIsa();
-
- boolean isOem();
-
- boolean isOverlayIsStatic();
-
- boolean isPrivileged();
-
- boolean isProduct();
-
- boolean isProfileableByShell();
-
- boolean isRequiredForAllUsers();
-
- boolean isStaticSharedLibrary();
-
- boolean isStub();
-
- boolean isSystem(); // TODO(b/135203078): Collapse with isSystemApp, should be exactly the same.
-
- boolean isSystemApp();
-
- boolean isSystemExt();
-
- boolean isUpdatedSystemApp();
-
- boolean isUse32BitAbi();
-
- boolean isVendor();
-
- boolean isVisibleToInstantApps();
-
- List<String> makeListAllCodePaths(); // TODO(b/135203078): Collapse with getAllCodePaths
-
- boolean requestsIsolatedSplitLoading();
-
- /**
- * Generates an {@link ApplicationInfo} object with only the data available in this object.
- *
- * This does not contain any system or user state data, and should be avoided. Prefer
- * {@link PackageInfoUtils#generateApplicationInfo(AndroidPackage, int, PackageUserState, int)}.
- */
- ApplicationInfo toAppInfoWithoutState();
-
- Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
- @Override
- public PackageImpl createFromParcel(Parcel source) {
- return new PackageImpl(source);
- }
-
- @Override
- public PackageImpl[] newArray(int size) {
- return new PackageImpl[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/parsing/AndroidPackageWrite.java b/core/java/android/content/pm/parsing/AndroidPackageWrite.java
deleted file mode 100644
index b7595d2..0000000
--- a/core/java/android/content/pm/parsing/AndroidPackageWrite.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 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.parsing;
-
-import android.annotation.Nullable;
-import android.content.pm.PackageParser;
-import android.content.pm.SharedLibraryInfo;
-
-import java.util.List;
-
-/**
- * Contains remaining mutable fields after package parsing has completed.
- *
- * Most are state that can probably be tracked outside of the AndroidPackage object. New methods
- * should never be added to this interface.
- *
- * TODO(b/135203078): Remove entirely
- *
- * @deprecated the eventual goal is that the object returned from parsing represents exactly what
- * was parsed from the APK, and so further mutation should be disallowed,
- * with any state being stored in another class
- *
- * @hide
- */
-@Deprecated
-public interface AndroidPackageWrite extends AndroidPackage {
-
- AndroidPackageWrite setUsesLibraryFiles(@Nullable String[] usesLibraryFiles);
-
- // TODO(b/135203078): Remove or use a non-system wide representation of the shared libraries;
- // this doesn't represent what was parsed from the APK
- AndroidPackageWrite setUsesLibraryInfos(@Nullable List<SharedLibraryInfo> usesLibraryInfos);
-
- AndroidPackageWrite setHiddenUntilInstalled(boolean hidden);
-
- AndroidPackageWrite setUpdatedSystemApp(boolean updatedSystemApp);
-
- AndroidPackageWrite setLastPackageUsageTimeInMills(int reason, long time);
-
- AndroidPackageWrite setPrimaryCpuAbi(String primaryCpuAbi);
-
- AndroidPackageWrite setSeInfo(String seInfo);
-
- AndroidPackageWrite setSigningDetails(PackageParser.SigningDetails signingDetails);
-}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 9087f42..4c6da03 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -50,7 +50,7 @@
/** @hide */
public class ApkLiteParseUtils {
- private static final String TAG = ApkParseUtils.TAG;
+ private static final String TAG = ParsingPackageUtils.TAG;
// TODO(b/135203078): Consolidate constants
private static final int DEFAULT_MIN_SDK_VERSION = 1;
@@ -235,7 +235,7 @@
final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
- signingDetails = ApkParseUtils.collectCertificates(apkFile.getAbsolutePath(),
+ signingDetails = ParsingPackageUtils.collectCertificates(apkFile.getAbsolutePath(),
skipVerify, false, PackageParser.SigningDetails.UNKNOWN,
DEFAULT_TARGET_SDK_VERSION);
} finally {
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
deleted file mode 100644
index 905794b..0000000
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ /dev/null
@@ -1,3349 +0,0 @@
-/*
- * Copyright (C) 2019 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.parsing;
-
-import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
-import static android.os.Build.VERSION_CODES.O;
-import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.app.ActivityThread;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.SigningDetails;
-import android.content.pm.Signature;
-import android.content.pm.permission.SplitPermissionInfoParcelable;
-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;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.FileUtils;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.ext.SdkExtensions;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.util.apk.ApkSignatureVerifier;
-
-import com.android.internal.R;
-import com.android.internal.os.ClassLoaderFactory;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-
-/** @hide */
-public class ApkParseUtils {
-
- // TODO(b/135203078): Consolidate log tags
- static final String TAG = "PackageParsing";
-
- /**
- * Parse the package at the given location. Automatically detects if the
- * package is a monolithic style (single APK file) or cluster style
- * (directory of APKs).
- * <p>
- * This performs sanity checking on cluster style packages, such as
- * requiring identical package name and version codes, a single base APK,
- * and unique split names.
- * <p>
- * Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #collectCertificates(ParsedPackage, boolean)}.
- *
- * If {@code useCaches} is true, the package parser might return a cached
- * result from a previous parse of the same {@code packageFile} with the same
- * {@code flags}. Note that this method does not check whether {@code packageFile}
- * has changed since the last parse, it's up to callers to do so.
- *
- * @see PackageParser#parsePackageLite(File, int)
- */
- public static ParsingPackage parsePackage(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- DisplayMetrics displayMetrics,
- boolean onlyCoreApps,
- File packageFile,
- int flags
- ) throws PackageParserException {
- if (packageFile.isDirectory()) {
- return parseClusterPackage(parseInput, separateProcesses, callback, displayMetrics,
- onlyCoreApps, packageFile, flags);
- } else {
- return parseMonolithicPackage(parseInput, separateProcesses, callback, displayMetrics,
- onlyCoreApps, packageFile, flags);
- }
- }
-
- /**
- * Parse all APKs contained in the given directory, treating them as a
- * single package. This also performs sanity checking, such as requiring
- * identical package name and version codes, a single base APK, and unique
- * split names.
- * <p>
- * Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #collectCertificates(ParsedPackage, boolean)}.
- */
- private static ParsingPackage parseClusterPackage(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- DisplayMetrics displayMetrics,
- boolean onlyCoreApps,
- File packageDir,
- int flags
- ) throws PackageParserException {
- final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
- 0);
- if (onlyCoreApps && !lite.coreApp) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Not a coreApp: " + packageDir);
- }
-
- // Build the split dependency tree.
- SparseArray<int[]> splitDependencies = null;
- final SplitAssetLoader assetLoader;
- if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
- try {
- splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
- assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
- } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
- }
- } else {
- assetLoader = new DefaultSplitAssetLoader(lite, flags);
- }
-
- try {
- final AssetManager assets = assetLoader.getBaseAssetManager();
- final File baseApk = new File(lite.baseCodePath);
- ParsingPackage parsingPackage = parseBaseApk(parseInput, separateProcesses, callback,
- displayMetrics, baseApk, assets, flags);
- if (parsingPackage == null) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Failed to parse base APK: " + baseApk);
- }
-
- if (!ArrayUtils.isEmpty(lite.splitNames)) {
- parsingPackage.asSplit(
- lite.splitNames,
- lite.splitCodePaths,
- lite.splitRevisionCodes,
- splitDependencies
- );
- final int num = lite.splitNames.length;
-
- for (int i = 0; i < num; i++) {
- final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
- parseSplitApk(parseInput, displayMetrics, separateProcesses, parsingPackage, i,
- splitAssets, flags);
- }
- }
-
- return parsingPackage.setCodePath(lite.codePath)
- .setUse32BitAbi(lite.use32bitAbi);
- } finally {
- IoUtils.closeQuietly(assetLoader);
- }
- }
-
- /**
- * Parse the given APK file, treating it as as a single monolithic package.
- * <p>
- * Note that this <em>does not</em> perform signature verification; that
- * must be done separately in {@link #collectCertificates(AndroidPackage, boolean)}.
- */
- public static ParsingPackage parseMonolithicPackage(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- DisplayMetrics displayMetrics,
- boolean onlyCoreApps,
- File apkFile,
- int flags
- ) throws PackageParserException {
- final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
- flags);
- if (onlyCoreApps) {
- if (!lite.coreApp) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Not a coreApp: " + apkFile);
- }
- }
-
- final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
- try {
- return parseBaseApk(parseInput, separateProcesses, callback,
- displayMetrics, apkFile, assetLoader.getBaseAssetManager(), flags)
- .setCodePath(apkFile.getCanonicalPath())
- .setUse32BitAbi(lite.use32bitAbi);
- } catch (IOException e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to get path: " + apkFile, e);
- } finally {
- IoUtils.closeQuietly(assetLoader);
- }
- }
-
- private static ParsingPackage parseBaseApk(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- DisplayMetrics displayMetrics,
- File apkFile,
- AssetManager assets,
- int flags
- ) throws PackageParserException {
- final String apkPath = apkFile.getAbsolutePath();
-
- String volumeUuid = null;
- if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
- final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
- volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
- }
-
- if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
-
- XmlResourceParser parser = null;
- try {
- 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, PackageParser.ANDROID_MANIFEST_FILENAME);
- final Resources res = new Resources(assets, displayMetrics, null);
-
- ParseResult result = parseBaseApk(parseInput, separateProcesses, callback, apkPath, res,
- parser, flags);
- if (!result.isSuccess()) {
- throw new PackageParserException(result.getParseError(),
- apkPath + " (at " + parser.getPositionDescription() + "): "
- + result.getErrorMessage());
- }
-
- ParsingPackage pkg = result.getResultAndNull();
- ApkAssets apkAssets = assets.getApkAssets()[0];
- if (apkAssets.definesOverlayable()) {
- SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
- int size = packageNames.size();
- for (int index = 0; index < size; index++) {
- String packageName = packageNames.get(index);
- Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
- if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
- for (String overlayable : overlayableToActor.keySet()) {
- pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
- }
- }
- }
- }
-
- return pkg.setVolumeUuid(volumeUuid)
- .setApplicationVolumeUuid(volumeUuid)
- .setSigningDetails(SigningDetails.UNKNOWN);
- } catch (PackageParserException e) {
- throw e;
- } catch (Exception e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to read manifest from " + apkPath, e);
- } finally {
- IoUtils.closeQuietly(parser);
- }
- }
-
- private static void parseSplitApk(
- ParseInput parseInput,
- DisplayMetrics displayMetrics,
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- int splitIndex,
- AssetManager assets,
- int flags
- ) throws PackageParserException {
- final String apkPath = parsingPackage.getSplitCodePaths()[splitIndex];
-
- if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
-
- final Resources res;
- XmlResourceParser parser = null;
- try {
- // 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, PackageParser.ANDROID_MANIFEST_FILENAME);
- res = new Resources(assets, displayMetrics, null);
-
- final String[] outError = new String[1];
- ParseResult parseResult = parseSplitApk(parseInput, separateProcesses, parsingPackage,
- res, parser, flags, splitIndex, outError);
- if (!parseResult.isSuccess()) {
- throw new PackageParserException(parseResult.getParseError(),
- apkPath + " (at " + parser.getPositionDescription() + "): "
- + parseResult.getErrorMessage());
- }
- } catch (PackageParserException e) {
- throw e;
- } catch (Exception e) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to read manifest from " + apkPath, e);
- } finally {
- IoUtils.closeQuietly(parser);
- }
- }
-
- /**
- * Parse the manifest of a <em>base APK</em>. When adding new features you
- * need to consider whether they should be supported by split APKs and child
- * packages.
- *
- * @param apkPath The package apk file path
- * @param res The resources from which to resolve values
- * @param parser The manifest parser
- * @param flags Flags how to parse
- * @return Parsed package or null on error.
- */
- private static ParseResult parseBaseApk(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- String apkPath,
- Resources res,
- XmlResourceParser parser,
- int flags
- ) throws XmlPullParserException, IOException {
- final String splitName;
- final String pkgName;
-
- try {
- Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser,
- parser);
- pkgName = packageSplit.first;
- splitName = packageSplit.second;
-
- if (!TextUtils.isEmpty(splitName)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
- "Expected base APK, but found split " + splitName
- );
- }
- } catch (PackageParserException e) {
- return parseInput.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
- }
-
- TypedArray manifestArray = null;
-
- try {
- manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
-
- boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
-
- ParsingPackage parsingPackage = PackageImpl.forParsing(
- pkgName,
- apkPath,
- manifestArray,
- isCoreApp
- );
-
- ParseResult result = parseBaseApkTags(parseInput, separateProcesses, callback,
- parsingPackage, manifestArray, res, parser, flags);
- if (!result.isSuccess()) {
- return result;
- }
-
- return parseInput.success(parsingPackage);
- } finally {
- if (manifestArray != null) {
- manifestArray.recycle();
- }
- }
- }
-
- /**
- * Parse the manifest of a <em>split APK</em>.
- * <p>
- * Note that split APKs have many more restrictions on what they're capable
- * of doing, so many valid features of a base APK have been carefully
- * omitted here.
- *
- * @param parsingPackage builder to fill
- * @return false on failure
- */
- private static ParseResult parseSplitApk(
- ParseInput parseInput,
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- int flags,
- int splitIndex,
- String[] outError
- ) throws XmlPullParserException, IOException, PackageParserException {
- AttributeSet attrs = parser;
-
- // We parsed manifest tag earlier; just skip past it
- PackageParser.parsePackageSplitNames(parser, attrs);
-
- int type;
-
- boolean foundApp = false;
-
- int outerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals(PackageParser.TAG_APPLICATION)) {
- if (foundApp) {
- if (PackageParser.RIGID_PARSER) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "<manifest> has more than one <application>"
- );
- } else {
- Slog.w(TAG, "<manifest> has more than one <application>");
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- }
-
- foundApp = true;
- ParseResult parseResult = parseSplitApplication(parseInput, separateProcesses,
- parsingPackage, res,
- parser, flags,
- splitIndex, outError);
- if (!parseResult.isSuccess()) {
- return parseResult;
- }
-
- } else if (PackageParser.RIGID_PARSER) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad element under <manifest>: " + parser.getName()
- );
-
- } else {
- Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- }
-
- if (!foundApp) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY,
- "<manifest> does not contain an <application>"
- );
- }
-
- return parseInput.success(parsingPackage);
- }
-
- /**
- * Parse the {@code application} XML tree at the current parse location in a
- * <em>split APK</em> manifest.
- * <p>
- * Note that split APKs have many more restrictions on what they're capable
- * of doing, so many valid features of a base APK have been carefully
- * omitted here.
- */
- private static ParseResult parseSplitApplication(
- ParseInput parseInput,
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- int flags,
- int splitIndex,
- String[] outError
- ) throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
-
- parsingPackage.setSplitHasCode(splitIndex, sa.getBoolean(
- R.styleable.AndroidManifestApplication_hasCode, true));
-
- final String classLoaderName = sa.getString(
- R.styleable.AndroidManifestApplication_classLoader);
- if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
- parsingPackage.setSplitClassLoaderName(splitIndex, classLoaderName);
- } else {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Invalid class loader name: " + classLoaderName
- );
- }
-
- final int innerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- ComponentParseUtils.ParsedComponent parsedComponent = null;
-
- String tagName = parser.getName();
- switch (tagName) {
- case "activity":
- ComponentParseUtils.ParsedActivity activity =
- ComponentParseUtils.parseActivity(separateProcesses,
- parsingPackage,
- res, parser, flags,
- outError,
- false,
- parsingPackage.isBaseHardwareAccelerated());
- if (activity == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addActivity(activity);
- parsedComponent = activity;
- break;
- case "receiver":
- activity = ComponentParseUtils.parseActivity(
- separateProcesses, parsingPackage,
- res, parser, flags, outError,
- true, false);
- if (activity == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addReceiver(activity);
- parsedComponent = activity;
- break;
- case "service":
- ComponentParseUtils.ParsedService s = ComponentParseUtils.parseService(
- separateProcesses,
- parsingPackage,
- res, parser, flags, outError
- );
- if (s == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addService(s);
- parsedComponent = s;
- break;
- case "provider":
- ComponentParseUtils.ParsedProvider p = ComponentParseUtils.parseProvider(
- separateProcesses,
- parsingPackage,
- res, parser, flags, outError);
- if (p == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addProvider(p);
- parsedComponent = p;
- break;
- case "activity-alias":
- activity = ComponentParseUtils.parseActivityAlias(
- parsingPackage,
- res,
- parser,
- outError
- );
- if (activity == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addActivity(activity);
- parsedComponent = activity;
- break;
- case "meta-data":
- // note: application meta-data is stored off to the side, so it can
- // remain null in the primary copy (we like to avoid extra copies because
- // it can be large)
- Bundle appMetaData = parseMetaData(parsingPackage, res, parser,
- parsingPackage.getAppMetaData(),
- outError);
- if (appMetaData == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.setAppMetaData(appMetaData);
- break;
- case "uses-static-library":
- ParseResult parseResult = parseUsesStaticLibrary(parseInput, parsingPackage,
- res, parser);
- if (!parseResult.isSuccess()) {
- return parseResult;
- }
-
- break;
- case "uses-library":
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- String lname = sa.getNonResourceString(
- R.styleable.AndroidManifestUsesLibrary_name);
- boolean req = sa.getBoolean(
- R.styleable.AndroidManifestUsesLibrary_required, true);
-
- sa.recycle();
-
- if (lname != null) {
- lname = lname.intern();
- if (req) {
- // Upgrade to treat as stronger constraint
- parsingPackage.addUsesLibrary(lname)
- .removeUsesOptionalLibrary(lname);
- } else {
- // Ignore if someone already defined as required
- if (!ArrayUtils.contains(parsingPackage.getUsesLibraries(), lname)) {
- parsingPackage.addUsesOptionalLibrary(lname);
- }
- }
- }
-
- XmlUtils.skipCurrentTag(parser);
- break;
- case "uses-package":
- // Dependencies for app installers; we don't currently try to
- // enforce this.
- XmlUtils.skipCurrentTag(parser);
- break;
- default:
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <application>: " + tagName
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad element under <application>: " + tagName
- );
- }
- }
-
- if (parsedComponent != null && parsedComponent.getSplitName() == null) {
- // If the loaded component did not specify a split, inherit the split name
- // based on the split it is defined in.
- // This is used to later load the correct split when starting this
- // component.
- parsedComponent.setSplitName(parsingPackage.getSplitNames()[splitIndex]);
- }
- }
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parseBaseApkTags(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- ParsingPackage parsingPackage,
- TypedArray manifestArray,
- Resources res,
- XmlResourceParser parser,
- int flags
- ) throws XmlPullParserException, IOException {
- int type;
- boolean foundApp = false;
-
- TypedArray sa = manifestArray;
-
- ParseResult sharedUserResult = parseSharedUser(parseInput, parsingPackage, sa);
- if (!sharedUserResult.isSuccess()) {
- return sharedUserResult;
- }
-
- parseManifestAttributes(sa, parsingPackage, flags);
-
- int outerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
-
- // All methods return a boolean, even if they can't fail. This can be enforced
- // by making this final and not assigned, forcing the switch to assign success
- // once in every branch.
- final boolean success;
- ParseResult parseResult = null;
-
- // TODO(b/135203078): Either use all booleans or all ParseResults
- // TODO(b/135203078): Convert to instance methods to share variables
- switch (tagName) {
- case PackageParser.TAG_APPLICATION:
- if (foundApp) {
- if (PackageParser.RIGID_PARSER) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "<manifest> has more than one <application>"
- );
- } else {
- Slog.w(TAG, "<manifest> has more than one <application>");
- XmlUtils.skipCurrentTag(parser);
- success = true;
- }
- } else {
- foundApp = true;
- parseResult = parseBaseApplication(parseInput, separateProcesses,
- callback,
- parsingPackage, res, parser, flags);
- success = parseResult.isSuccess();
- }
- break;
- case PackageParser.TAG_OVERLAY:
- parseResult = parseOverlay(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_KEY_SETS:
- parseResult = parseKeySets(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_FEATURE:
- parseResult = parseFeature(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_PERMISSION_GROUP:
- parseResult = parsePermissionGroup(parseInput, parsingPackage, res,
- parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_PERMISSION:
- parseResult = parsePermission(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_PERMISSION_TREE:
- parseResult = parsePermissionTree(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_USES_PERMISSION:
- case PackageParser.TAG_USES_PERMISSION_SDK_M:
- case PackageParser.TAG_USES_PERMISSION_SDK_23:
- parseResult = parseUsesPermission(parseInput, parsingPackage, res, parser,
- callback);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_USES_CONFIGURATION:
- success = parseUsesConfiguration(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_USES_FEATURE:
- success = parseUsesFeature(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_FEATURE_GROUP:
- success = parseFeatureGroup(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_USES_SDK:
- parseResult = parseUsesSdk(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_SUPPORT_SCREENS:
- success = parseSupportScreens(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_PROTECTED_BROADCAST:
- success = parseProtectedBroadcast(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_INSTRUMENTATION:
- parseResult = parseInstrumentation(parseInput, parsingPackage, res,
- parser);
- success = parseResult.isSuccess();
- break;
- case PackageParser.TAG_ORIGINAL_PACKAGE:
- success = parseOriginalPackage(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_ADOPT_PERMISSIONS:
- success = parseAdoptPermissions(parsingPackage, res, parser);
- break;
- case PackageParser.TAG_USES_GL_TEXTURE:
- case PackageParser.TAG_COMPATIBLE_SCREENS:
- case PackageParser.TAG_SUPPORTS_INPUT:
- case PackageParser.TAG_EAT_COMMENT:
- // Just skip this tag
- XmlUtils.skipCurrentTag(parser);
- success = true;
- break;
- case PackageParser.TAG_RESTRICT_UPDATE:
- success = parseRestrictUpdateHash(flags, parsingPackage, res, parser);
- break;
- case PackageParser.TAG_QUERIES:
- parseResult = parseQueries(parseInput, parsingPackage, res, parser);
- success = parseResult.isSuccess();
- break;
- default:
- parseResult = parseUnknownTag(parseInput, parsingPackage, parser);
- success = parseResult.isSuccess();
- break;
- }
-
- if (parseResult != null && !parseResult.isSuccess()) {
- return parseResult;
- }
-
- if (!success) {
- return parseResult;
- }
- }
-
- if (!foundApp && ArrayUtils.size(parsingPackage.getInstrumentations()) == 0) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY,
- "<manifest> does not contain an <application> or <instrumentation>"
- );
- }
-
- if (!ComponentParseUtils.ParsedFeature.isCombinationValid(parsingPackage.getFeatures())) {
- return parseInput.error(
- INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Combination <feature> tags are not valid"
- );
- }
-
- convertNewPermissions(parsingPackage);
-
- convertSplitPermissions(parsingPackage);
-
- // At this point we can check if an application is not supporting densities and hence
- // cannot be windowed / resized. Note that an SDK version of 0 is common for
- // pre-Doughnut applications.
- if (parsingPackage.usesCompatibilityMode()) {
- adjustPackageToBeUnresizeableAndUnpipable(parsingPackage);
- }
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parseUnknownTag(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- if (PackageParser.RIGID_PARSER) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad element under <manifest>: " + parser.getName()
- );
- } else {
- Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- return parseInput.success(parsingPackage);
- }
- }
-
- private static ParseResult parseSharedUser(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- TypedArray manifestArray
- ) {
- String str = manifestArray.getNonConfigurationString(
- R.styleable.AndroidManifest_sharedUserId, 0);
- if (TextUtils.isEmpty(str)) {
- return parseInput.success(parsingPackage);
- }
-
- String nameError = validateName(str, true, true);
- if (nameError != null && !"android".equals(parsingPackage.getPackageName())) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
- "<manifest> specifies bad sharedUserId name \"" + str + "\": "
- + nameError
- );
- }
-
- int sharedUserLabel = manifestArray.getResourceId(
- R.styleable.AndroidManifest_sharedUserLabel, 0);
- parsingPackage.setSharedUserId(str.intern())
- .setSharedUserLabel(sharedUserLabel);
-
- return parseInput.success(parsingPackage);
- }
-
- private static void parseManifestAttributes(
- TypedArray manifestArray,
- ParsingPackage parsingPackage,
- int flags
- ) {
- int installLocation = manifestArray.getInteger(R.styleable.AndroidManifest_installLocation,
- PackageParser.PARSE_DEFAULT_INSTALL_LOCATION);
-
- final int targetSandboxVersion = manifestArray.getInteger(
- R.styleable.AndroidManifest_targetSandboxVersion,
- PackageParser.PARSE_DEFAULT_TARGET_SANDBOX);
-
- parsingPackage.setInstallLocation(installLocation)
- .setTargetSandboxVersion(targetSandboxVersion);
-
- /* Set the global "on SD card" flag */
- parsingPackage.setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
-
- parsingPackage.setIsolatedSplitLoading(manifestArray.getBoolean(
- R.styleable.AndroidManifest_isolatedSplits, false));
- }
-
- private static ParseResult parseKeySets(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws XmlPullParserException, IOException {
- // we've encountered the 'key-sets' tag
- // all the keys and keysets that we want must be defined here
- // so we're going to iterate over the parser and pull out the things we want
- int outerDepth = parser.getDepth();
- int currentKeySetDepth = -1;
- int type;
- String currentKeySet = null;
- ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
- ArraySet<String> upgradeKeySets = new ArraySet<>();
- ArrayMap<String, ArraySet<String>> definedKeySets =
- new ArrayMap<>();
- ArraySet<String> improperKeySets = new ArraySet<>();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG) {
- if (parser.getDepth() == currentKeySetDepth) {
- currentKeySet = null;
- currentKeySetDepth = -1;
- }
- continue;
- }
- String tagName = parser.getName();
- if (tagName.equals("key-set")) {
- if (currentKeySet != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Improperly nested 'key-set' tag at " + parser.getPositionDescription()
- );
- }
- final TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestKeySet);
- final String keysetName = sa.getNonResourceString(
- R.styleable.AndroidManifestKeySet_name);
- definedKeySets.put(keysetName, new ArraySet<>());
- currentKeySet = keysetName;
- currentKeySetDepth = parser.getDepth();
- sa.recycle();
- } else if (tagName.equals("public-key")) {
- if (currentKeySet == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Improperly nested 'key-set' tag at " + parser.getPositionDescription()
- );
- }
- final TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestPublicKey);
- final String publicKeyName = sa.getNonResourceString(
- R.styleable.AndroidManifestPublicKey_name);
- final String encodedKey = sa.getNonResourceString(
- R.styleable.AndroidManifestPublicKey_value);
- if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
- sa.recycle();
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "'public-key' " + publicKeyName + " must define a public-key value"
- + " on first use at " + parser.getPositionDescription()
- );
- } else if (encodedKey != null) {
- PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
- if (currentKey == null) {
- Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
- + parser.getPositionDescription() + " key-set " + currentKeySet
- + " will not be added to the package's defined key-sets.");
- sa.recycle();
- improperKeySets.add(currentKeySet);
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- if (publicKeys.get(publicKeyName) == null
- || publicKeys.get(publicKeyName).equals(currentKey)) {
-
- /* public-key first definition, or matches old definition */
- publicKeys.put(publicKeyName, currentKey);
- } else {
- sa.recycle();
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Value of 'public-key' " + publicKeyName
- + " conflicts with previously defined value at "
- + parser.getPositionDescription()
- );
- }
- }
- definedKeySets.get(currentKeySet).add(publicKeyName);
- sa.recycle();
- XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("upgrade-key-set")) {
- final TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestUpgradeKeySet);
- String name = sa.getNonResourceString(
- R.styleable.AndroidManifestUpgradeKeySet_name);
- upgradeKeySets.add(name);
- sa.recycle();
- XmlUtils.skipCurrentTag(parser);
- } else if (PackageParser.RIGID_PARSER) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad element under <key-sets>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription()
- );
- } else {
- Slog.w(TAG, "Unknown element under <key-sets>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- }
- String packageName = parsingPackage.getPackageName();
- Set<String> publicKeyNames = publicKeys.keySet();
- if (publicKeyNames.removeAll(definedKeySets.keySet())) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Package" + packageName + " AndroidManifest.xml "
- + "'key-set' and 'public-key' names must be distinct."
- );
- }
-
- for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
- final String keySetName = e.getKey();
- if (e.getValue().size() == 0) {
- Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
- + "'key-set' " + keySetName + " has no valid associated 'public-key'."
- + " Not including in package's defined key-sets.");
- continue;
- } else if (improperKeySets.contains(keySetName)) {
- Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
- + "'key-set' " + keySetName + " contained improper 'public-key'"
- + " tags. Not including in package's defined key-sets.");
- continue;
- }
-
- for (String s : e.getValue()) {
- parsingPackage.addKeySet(keySetName, publicKeys.get(s));
- }
- }
- if (parsingPackage.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
- parsingPackage.setUpgradeKeySets(upgradeKeySets);
- } else {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Package" + packageName + " AndroidManifest.xml "
- + "does not define all 'upgrade-key-set's ."
- );
- }
-
- return parseInput.success(parsingPackage);
- }
-
- public static boolean parsePackageItemInfo(String packageName, PackageItemInfo outInfo,
- String[] outError, String tag, TypedArray sa, boolean nameRequired,
- int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
- // This case can only happen in unit tests where we sometimes need to create fakes
- // of various package parser data structures.
- if (sa == null) {
- outError[0] = tag + " does not contain any attributes";
- return false;
- }
-
- String name = sa.getNonConfigurationString(nameRes, 0);
- if (name == null) {
- if (nameRequired) {
- outError[0] = tag + " does not specify android:name";
- return false;
- }
- } else {
- String outInfoName = buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
- outError[0] = tag + " invalid android:name";
- return false;
- }
- outInfo.name = outInfoName;
- if (outInfoName == null) {
- return false;
- }
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
- if (roundIconVal != 0) {
- outInfo.icon = roundIconVal;
- outInfo.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(iconRes, 0);
- if (iconVal != 0) {
- outInfo.icon = iconVal;
- outInfo.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(logoRes, 0);
- if (logoVal != 0) {
- outInfo.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(bannerRes, 0);
- if (bannerVal != 0) {
- outInfo.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(labelRes);
- if (v != null && (outInfo.labelRes = v.resourceId) == 0) {
- outInfo.nonLocalizedLabel = v.coerceToString();
- }
-
- outInfo.packageName = packageName;
-
- return true;
- }
-
- private static ParseResult parsePackageItemInfo(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- String tag,
- TypedArray sa,
- boolean nameRequired,
- int nameRes,
- int labelRes,
- int iconRes,
- int roundIconRes,
- int logoRes,
- int bannerRes
- ) {
- // This case can only happen in unit tests where we sometimes need to create fakes
- // of various package parser data structures.
- if (sa == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- tag + " does not contain any attributes"
- );
- }
-
- String name = sa.getNonConfigurationString(nameRes, 0);
- if (name == null) {
- if (nameRequired) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- tag + " does not specify android:name"
- );
- }
- } else {
- String packageName = parsingPackage.getPackageName();
- String outInfoName = buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- tag + " invalid android:name"
- );
- } else if (outInfoName == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Empty class name in package " + packageName
- );
- }
-
- parsingPackage.setName(outInfoName);
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
- if (roundIconVal != 0) {
- parsingPackage.setIcon(roundIconVal)
- .setNonLocalizedLabel(null);
- } else {
- int iconVal = sa.getResourceId(iconRes, 0);
- if (iconVal != 0) {
- parsingPackage.setIcon(iconVal)
- .setNonLocalizedLabel(null);
- }
- }
-
- int logoVal = sa.getResourceId(logoRes, 0);
- if (logoVal != 0) {
- parsingPackage.setLogo(logoVal);
- }
-
- int bannerVal = sa.getResourceId(bannerRes, 0);
- if (bannerVal != 0) {
- parsingPackage.setBanner(bannerVal);
- }
-
- TypedValue v = sa.peekValue(labelRes);
- if (v != null) {
- parsingPackage.setLabelRes(v.resourceId);
- if (v.resourceId == 0) {
- parsingPackage.setNonLocalizedLabel(v.coerceToString());
- }
- }
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parseFeature(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
-
- ComponentParseUtils.ParsedFeature parsedFeature =
- ComponentParseUtils.parseFeature(res, parser, outError);
-
- if (parsedFeature == null || outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addFeature(parsedFeature);
-
- return parseInput.success(parsingPackage);
- }
-
-
- private static ParseResult parsePermissionGroup(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws XmlPullParserException, IOException {
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
-
- ComponentParseUtils.ParsedPermissionGroup parsedPermissionGroup =
- ComponentParseUtils.parsePermissionGroup(parsingPackage,
- res, parser, outError);
-
- if (parsedPermissionGroup == null || outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addPermissionGroup(parsedPermissionGroup);
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parsePermission(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws XmlPullParserException, IOException {
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
-
- ComponentParseUtils.ParsedPermission parsedPermission =
- ComponentParseUtils.parsePermission(parsingPackage,
- res, parser, outError);
-
- if (parsedPermission == null || outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addPermission(parsedPermission);
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parsePermissionTree(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws XmlPullParserException, IOException {
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
-
- ComponentParseUtils.ParsedPermission parsedPermission =
- ComponentParseUtils.parsePermissionTree(parsingPackage,
- res, parser, outError);
-
- if (parsedPermission == null || outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addPermission(parsedPermission);
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parseUsesPermission(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- PackageParser.Callback callback
- )
- throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestUsesPermission);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- String name = sa.getNonResourceString(
- R.styleable.AndroidManifestUsesPermission_name);
-
- int maxSdkVersion = 0;
- TypedValue val = sa.peekValue(
- R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
- if (val != null) {
- if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
- maxSdkVersion = val.data;
- }
- }
-
- final String requiredFeature = sa.getNonConfigurationString(
- R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
-
- final String requiredNotfeature = sa.getNonConfigurationString(
- R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
- 0);
-
- sa.recycle();
-
- XmlUtils.skipCurrentTag(parser);
-
- // Can only succeed from here on out
- ParseResult success = parseInput.success(parsingPackage);
-
- if (name == null) {
- return success;
- }
-
- if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
- return success;
- }
-
- // Only allow requesting this permission if the platform supports the given feature.
- if (requiredFeature != null && callback != null && !callback.hasFeature(requiredFeature)) {
- return success;
- }
-
- // Only allow requesting this permission if the platform doesn't support the given feature.
- if (requiredNotfeature != null && callback != null
- && callback.hasFeature(requiredNotfeature)) {
- return success;
- }
-
- if (!parsingPackage.getRequestedPermissions().contains(name)) {
- parsingPackage.addRequestedPermission(name.intern());
- } else {
- Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
- + name + " in package: " + parsingPackage.getPackageName() + " at: "
- + parser.getPositionDescription());
- }
-
- return success;
- }
-
- private static boolean parseUsesConfiguration(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- ConfigurationInfo cPref = new ConfigurationInfo();
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestUsesConfiguration);
- cPref.reqTouchScreen = sa.getInt(
- R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
- Configuration.TOUCHSCREEN_UNDEFINED);
- cPref.reqKeyboardType = sa.getInt(
- R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
- Configuration.KEYBOARD_UNDEFINED);
- if (sa.getBoolean(
- R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
- false)) {
- cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
- }
- cPref.reqNavigation = sa.getInt(
- R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
- Configuration.NAVIGATION_UNDEFINED);
- if (sa.getBoolean(
- R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
- false)) {
- cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
- }
- sa.recycle();
- parsingPackage.addConfigPreference(cPref);
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static boolean parseUsesFeature(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- FeatureInfo fi = parseFeatureInfo(res, parser);
- parsingPackage.addReqFeature(fi);
-
- if (fi.name == null) {
- ConfigurationInfo cPref = new ConfigurationInfo();
- cPref.reqGlEsVersion = fi.reqGlEsVersion;
- parsingPackage.addConfigPreference(cPref);
- }
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
- FeatureInfo fi = new FeatureInfo();
- TypedArray sa = res.obtainAttributes(attrs,
- R.styleable.AndroidManifestUsesFeature);
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
- fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
- if (fi.name == null) {
- fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
- FeatureInfo.GL_ES_VERSION_UNDEFINED);
- }
- if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
- fi.flags |= FeatureInfo.FLAG_REQUIRED;
- }
- sa.recycle();
- return fi;
- }
-
- private static boolean parseFeatureGroup(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- FeatureGroupInfo group = new FeatureGroupInfo();
- ArrayList<FeatureInfo> features = null;
- final int innerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- final String innerTagName = parser.getName();
- if (innerTagName.equals("uses-feature")) {
- FeatureInfo featureInfo = parseFeatureInfo(res, parser);
- // FeatureGroups are stricter and mandate that
- // any <uses-feature> declared are mandatory.
- featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
- features = ArrayUtils.add(features, featureInfo);
- } else {
- Slog.w(TAG,
- "Unknown element under <feature-group>: " + innerTagName +
- " at " + parsingPackage.getBaseCodePath() + " " +
- parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- }
-
- if (features != null) {
- group.features = new FeatureInfo[features.size()];
- group.features = features.toArray(group.features);
- }
-
- parsingPackage.addFeatureGroup(group);
- return true;
- }
-
- private static ParseResult parseUsesSdk(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- if (PackageParser.SDK_VERSION > 0) {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestUsesSdk);
-
- int minVers = 1;
- String minCode = null;
- int targetVers = 0;
- String targetCode = null;
-
- TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
- if (val != null) {
- if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- minCode = val.string.toString();
- } else {
- // If it's not a string, it's an integer.
- minVers = val.data;
- }
- }
-
- val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
- if (val != null) {
- if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- targetCode = val.string.toString();
- if (minCode == null) {
- minCode = targetCode;
- }
- } else {
- // If it's not a string, it's an integer.
- targetVers = val.data;
- }
- } else {
- targetVers = minVers;
- targetCode = minCode;
- }
-
- sa.recycle();
-
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
- final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers,
- minCode,
- PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, outError);
- if (minSdkVersion < 0) {
- return parseInput.error(
- PackageManager.INSTALL_FAILED_OLDER_SDK
- );
- }
-
- final int targetSdkVersion = PackageParser.computeTargetSdkVersion(
- targetVers,
- targetCode, PackageParser.SDK_CODENAMES, outError);
- if (targetSdkVersion < 0) {
- return parseInput.error(
- PackageManager.INSTALL_FAILED_OLDER_SDK
- );
- }
-
- int type;
- final int innerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (parser.getName().equals("extension-sdk")) {
- final ParseResult result =
- parseExtensionSdk(parseInput, parsingPackage, res, parser);
- if (!result.isSuccess()) {
- return result;
- }
- } else {
- Slog.w(TAG, "Unknown element under <uses-sdk>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- }
-
- parsingPackage.setMinSdkVersion(minSdkVersion)
- .setTargetSdkVersion(targetSdkVersion);
- }
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parseExtensionSdk(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser,
- com.android.internal.R.styleable.AndroidManifestExtensionSdk);
- int sdkVersion = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
- int minVersion = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestExtensionSdk_minExtensionVersion,
- -1);
- sa.recycle();
-
- if (sdkVersion < 0) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "<extension-sdk> must specify an sdkVersion >= 0");
- }
- if (minVersion < 0) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "<extension-sdk> must specify minExtensionVersion >= 0");
- }
-
- try {
- int version = SdkExtensions.getExtensionVersion(sdkVersion);
- if (version < minVersion) {
- return parseInput.error(
- PackageManager.INSTALL_FAILED_OLDER_SDK,
- "Package requires " + sdkVersion + " extension version " + minVersion
- + " which exceeds device version " + version);
- }
- } catch (RuntimeException e) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Specified sdkVersion " + sdkVersion + " is not valid");
- }
- return parseInput.success(parsingPackage);
- }
-
- private static boolean parseRestrictUpdateHash(
- int flags,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestRestrictUpdate);
- final String hash = sa.getNonConfigurationString(
- R.styleable.AndroidManifestRestrictUpdate_hash,
- 0);
- sa.recycle();
-
- if (hash != null) {
- final int hashLength = hash.length();
- final byte[] hashBytes = new byte[hashLength / 2];
- for (int i = 0; i < hashLength; i += 2) {
- hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
- << 4)
- + Character.digit(hash.charAt(i + 1), 16));
- }
- parsingPackage.setRestrictUpdateHash(hashBytes);
- } else {
- parsingPackage.setRestrictUpdateHash(null);
- }
- }
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static ParseResult parseQueries(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
-
- final int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (parser.getName().equals("intent")) {
- String[] outError = new String[1];
- ComponentParseUtils.ParsedQueriesIntentInfo intentInfo =
- ComponentParseUtils.parsedParsedQueriesIntentInfo(
- parsingPackage, res, parser, outError
- );
- if (intentInfo == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- Uri data = null;
- String dataType = null;
- String host = "";
- final int numActions = intentInfo.countActions();
- final int numSchemes = intentInfo.countDataSchemes();
- final int numTypes = intentInfo.countDataTypes();
- final int numHosts = intentInfo.getHosts().length;
- if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
- outError[0] = "intent tags must contain either an action or data.";
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
- if (numActions > 1) {
- outError[0] = "intent tag may have at most one action.";
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
- if (numTypes > 1) {
- outError[0] = "intent tag may have at most one data type.";
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
- if (numSchemes > 1) {
- outError[0] = "intent tag may have at most one data scheme.";
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
- if (numHosts > 1) {
- outError[0] = "intent tag may have at most one data host.";
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
- Intent intent = new Intent();
- for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
- intent.addCategory(intentInfo.getCategory(i));
- }
- if (numHosts == 1) {
- host = intentInfo.getHosts()[0];
- }
- if (numSchemes == 1) {
- data = new Uri.Builder()
- .scheme(intentInfo.getDataScheme(0))
- .authority(host)
- .build();
- }
- if (numTypes == 1) {
- dataType = intentInfo.getDataType(0);
- }
- intent.setDataAndType(data, dataType);
- if (numActions == 1) {
- intent.setAction(intentInfo.getAction(0));
- }
- parsingPackage.addQueriesIntent(intent);
- } else if (parser.getName().equals("package")) {
- final TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestQueriesPackage);
- final String packageName =
- sa.getString(R.styleable.AndroidManifestQueriesPackage_name);
- if (TextUtils.isEmpty(packageName)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Package name is missing from package tag."
- );
- }
- parsingPackage.addQueriesPackage(packageName.intern());
- } else if (parser.getName().equals("provider")) {
- final TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestQueriesProvider);
- try {
- final String authorities =
- sa.getString(R.styleable.AndroidManifestQueriesProvider_authorities);
- if (TextUtils.isEmpty(authorities)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Authority missing from provider tag."
- );
- }
- StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
- while (authoritiesTokenizer.hasMoreElements()) {
- parsingPackage.addQueriesProvider(authoritiesTokenizer.nextToken());
- }
- } finally {
- sa.recycle();
- }
- }
- }
- return parseInput.success(parsingPackage);
- }
-
- /**
- * Parse the {@code application} XML tree at the current parse location in a
- * <em>base APK</em> manifest.
- * <p>
- * When adding new features, carefully consider if they should also be
- * supported by split APKs.
- *
- * @hide
- */
- public static ParseResult parseBaseApplication(
- ParseInput parseInput,
- String[] separateProcesses,
- PackageParser.Callback callback,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- int flags
- ) throws XmlPullParserException, IOException {
- final String pkgName = parsingPackage.getPackageName();
-
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
- TypedArray sa = null;
-
- try {
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestApplication);
-
-
- parsingPackage
- .setIconRes(
- sa.getResourceId(R.styleable.AndroidManifestApplication_icon, 0))
- .setRoundIconRes(
- sa.getResourceId(R.styleable.AndroidManifestApplication_roundIcon, 0));
-
- ParseResult result = parsePackageItemInfo(
- parseInput,
- parsingPackage,
- "<application>",
- sa, false /*nameRequired*/,
- R.styleable.AndroidManifestApplication_name,
- R.styleable.AndroidManifestApplication_label,
- R.styleable.AndroidManifestApplication_icon,
- R.styleable.AndroidManifestApplication_roundIcon,
- R.styleable.AndroidManifestApplication_logo,
- R.styleable.AndroidManifestApplication_banner
- );
- if (!result.isSuccess()) {
- return result;
- }
-
- String name = parsingPackage.getName();
- if (name != null) {
- parsingPackage.setClassName(name);
- }
-
- String manageSpaceActivity = sa.getNonConfigurationString(
- R.styleable.AndroidManifestApplication_manageSpaceActivity,
- Configuration.NATIVE_CONFIG_VERSION);
- if (manageSpaceActivity != null) {
- String manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity);
-
- if (manageSpaceActivityName == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Empty class name in package " + pkgName
- );
- }
-
- parsingPackage.setManageSpaceActivityName(manageSpaceActivityName);
- }
-
- boolean allowBackup = sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowBackup, true);
- parsingPackage.setAllowBackup(allowBackup);
-
- if (allowBackup) {
- // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
- // and restoreAnyVersion are only relevant if backup is possible for the
- // given application.
- String backupAgent = sa.getNonConfigurationString(
- R.styleable.AndroidManifestApplication_backupAgent,
- Configuration.NATIVE_CONFIG_VERSION);
- if (backupAgent != null) {
- String backupAgentName = buildClassName(pkgName, backupAgent);
- if (backupAgentName == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Empty class name in package " + pkgName
- );
- }
-
- if (PackageParser.DEBUG_BACKUP) {
- Slog.v(TAG, "android:backupAgent = " + backupAgentName
- + " from " + pkgName + "+" + backupAgent);
- }
-
- parsingPackage.setBackupAgentName(backupAgentName);
-
- parsingPackage.setKillAfterRestore(sa.getBoolean(
- R.styleable.AndroidManifestApplication_killAfterRestore, true));
-
- parsingPackage.setRestoreAnyVersion(sa.getBoolean(
- R.styleable.AndroidManifestApplication_restoreAnyVersion, false));
-
- parsingPackage.setFullBackupOnly(sa.getBoolean(
- R.styleable.AndroidManifestApplication_fullBackupOnly, false));
-
- parsingPackage.setBackupInForeground(sa.getBoolean(
- R.styleable.AndroidManifestApplication_backupInForeground,
- false));
- }
-
- TypedValue v = sa.peekValue(
- R.styleable.AndroidManifestApplication_fullBackupContent);
- int fullBackupContent = 0;
-
- if (v != null) {
- fullBackupContent = v.resourceId;
-
- if (v.resourceId == 0) {
- if (PackageParser.DEBUG_BACKUP) {
- Slog.v(TAG, "fullBackupContent specified as boolean=" +
- (v.data == 0 ? "false" : "true"));
- }
- // "false" => -1, "true" => 0
- fullBackupContent = v.data == 0 ? -1 : 0;
- }
-
- parsingPackage.setFullBackupContent(fullBackupContent);
- }
- if (PackageParser.DEBUG_BACKUP) {
- Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
- }
- }
-
- parsingPackage
- .setTheme(
- sa.getResourceId(R.styleable.AndroidManifestApplication_theme, 0))
- .setDescriptionRes(
- sa.getResourceId(R.styleable.AndroidManifestApplication_description,
- 0));
-
- if (sa.getBoolean(
- R.styleable.AndroidManifestApplication_persistent,
- false)) {
- // Check if persistence is based on a feature being present
- final String requiredFeature = sa.getNonResourceString(R.styleable
- .AndroidManifestApplication_persistentWhenFeatureAvailable);
- parsingPackage.setPersistent(requiredFeature == null
- || callback.hasFeature(requiredFeature));
- }
-
- boolean requiredForAllUsers = sa.getBoolean(
- R.styleable.AndroidManifestApplication_requiredForAllUsers,
- false);
- parsingPackage.setRequiredForAllUsers(requiredForAllUsers);
-
- String restrictedAccountType = sa.getString(R.styleable
- .AndroidManifestApplication_restrictedAccountType);
- if (restrictedAccountType != null && restrictedAccountType.length() > 0) {
- parsingPackage.setRestrictedAccountType(restrictedAccountType);
- }
-
- String requiredAccountType = sa.getString(R.styleable
- .AndroidManifestApplication_requiredAccountType);
- if (requiredAccountType != null && requiredAccountType.length() > 0) {
- parsingPackage.setRequiredAccountType(requiredAccountType);
- }
-
- parsingPackage.setForceQueryable(
- sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false)
- );
-
- boolean debuggable = sa.getBoolean(
- R.styleable.AndroidManifestApplication_debuggable,
- false
- );
-
- parsingPackage.setDebuggable(debuggable);
-
- if (debuggable) {
- // Debuggable implies profileable
- parsingPackage.setProfileableByShell(true);
- }
-
- parsingPackage.setVmSafeMode(sa.getBoolean(
- R.styleable.AndroidManifestApplication_vmSafeMode, false));
-
- boolean baseHardwareAccelerated = sa.getBoolean(
- R.styleable.AndroidManifestApplication_hardwareAccelerated,
- parsingPackage.getTargetSdkVersion()
- >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
- parsingPackage.setBaseHardwareAccelerated(baseHardwareAccelerated);
-
- parsingPackage.setHasCode(sa.getBoolean(
- R.styleable.AndroidManifestApplication_hasCode, true));
-
- parsingPackage.setAllowTaskReparenting(sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowTaskReparenting, false));
-
- parsingPackage.setAllowClearUserData(sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowClearUserData, true));
-
- parsingPackage.setTestOnly(sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
- false));
-
- parsingPackage.setLargeHeap(sa.getBoolean(
- R.styleable.AndroidManifestApplication_largeHeap, false));
-
- parsingPackage.setUsesCleartextTraffic(sa.getBoolean(
- R.styleable.AndroidManifestApplication_usesCleartextTraffic,
- parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.P));
-
- parsingPackage.setSupportsRtl(sa.getBoolean(
- R.styleable.AndroidManifestApplication_supportsRtl,
- false /* default is no RTL support*/));
-
- parsingPackage.setMultiArch(sa.getBoolean(
- R.styleable.AndroidManifestApplication_multiArch, false));
-
- parsingPackage.setExtractNativeLibs(sa.getBoolean(
- R.styleable.AndroidManifestApplication_extractNativeLibs, true));
-
- parsingPackage.setUseEmbeddedDex(sa.getBoolean(
- R.styleable.AndroidManifestApplication_useEmbeddedDex, false));
-
- parsingPackage.setDefaultToDeviceProtectedStorage(sa.getBoolean(
- R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
- false));
-
- parsingPackage.setDirectBootAware(sa.getBoolean(
- R.styleable.AndroidManifestApplication_directBootAware, false));
-
- if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
- parsingPackage.setActivitiesResizeModeResizeable(sa.getBoolean(
- R.styleable.AndroidManifestApplication_resizeableActivity, true));
- } else {
- parsingPackage.setActivitiesResizeModeResizeableViaSdkVersion(
- parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.N);
- }
-
- parsingPackage.setAllowClearUserDataOnFailedRestore(sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore,
- true));
-
-
- parsingPackage.setAllowAudioPlaybackCapture(sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture,
- parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q));
-
- parsingPackage.setRequestLegacyExternalStorage(sa.getBoolean(
- R.styleable.AndroidManifestApplication_requestLegacyExternalStorage,
- parsingPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q));
-
- parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
- R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
-
- parsingPackage.setPreserveLegacyExternalStorage(sa.getBoolean(
- R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage,
- false));
-
- parsingPackage
- .setMaxAspectRatio(
- sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
- .setMinAspectRatio(
- sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0))
- .setNetworkSecurityConfigRes(sa.getResourceId(
- R.styleable.AndroidManifestApplication_networkSecurityConfig, 0))
- .setCategory(sa.getInt(R.styleable.AndroidManifestApplication_appCategory,
- ApplicationInfo.CATEGORY_UNDEFINED));
-
- String str;
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestApplication_permission, 0);
- parsingPackage.setPermission((str != null && str.length() > 0) ? str.intern() : null);
-
- if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestApplication_taskAffinity,
- Configuration.NATIVE_CONFIG_VERSION);
- } else {
- // Some older apps have been seen to use a resource reference
- // here that on older builds was ignored (with a warning). We
- // need to continue to do this for them so they don't break.
- str = sa.getNonResourceString(
- R.styleable.AndroidManifestApplication_taskAffinity);
- }
- String packageName = parsingPackage.getPackageName();
- String taskAffinity = PackageParser.buildTaskAffinityName(packageName,
- packageName,
- str, outError);
-
- if (outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.setTaskAffinity(taskAffinity);
- String factory = sa.getNonResourceString(
- R.styleable.AndroidManifestApplication_appComponentFactory);
- if (factory != null) {
- String appComponentFactory = buildClassName(packageName, factory);
- if (appComponentFactory == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Empty class name in package " + pkgName
- );
- }
-
- parsingPackage.setAppComponentFactory(appComponentFactory);
- }
-
- parsingPackage.setUsesNonSdkApi(sa.getBoolean(
- R.styleable.AndroidManifestApplication_usesNonSdkApi, false));
-
- parsingPackage.setHasFragileUserData(sa.getBoolean(
- R.styleable.AndroidManifestApplication_hasFragileUserData, false));
-
- CharSequence pname;
- if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
- pname = sa.getNonConfigurationString(
- R.styleable.AndroidManifestApplication_process,
- Configuration.NATIVE_CONFIG_VERSION);
- } else {
- // Some older apps have been seen to use a resource reference
- // here that on older builds was ignored (with a warning). We
- // need to continue to do this for them so they don't break.
- pname = sa.getNonResourceString(
- R.styleable.AndroidManifestApplication_process);
- }
- String processName = PackageParser.buildProcessName(packageName, null, pname, flags,
- separateProcesses, outError);
-
- if (outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage
- .setProcessName(processName)
- .setEnabled(
- sa.getBoolean(R.styleable.AndroidManifestApplication_enabled,
- true));
-
- parsingPackage.setCrossProfile(
- sa.getBoolean(R.styleable.AndroidManifestApplication_crossProfile, false));
-
- parsingPackage.setIsGame(sa.getBoolean(
- R.styleable.AndroidManifestApplication_isGame, false));
-
- boolean cantSaveState = sa.getBoolean(
- R.styleable.AndroidManifestApplication_cantSaveState, false);
- parsingPackage.setCantSaveState(cantSaveState);
- if (cantSaveState) {
- // A heavy-weight application can not be in a custom process.
- // We can do direct compare because we intern all strings.
- if (processName != null && !processName.equals(packageName)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "cantSaveState applications can not use custom processes"
- );
- }
- }
-
- String classLoaderName = sa.getString(
- R.styleable.AndroidManifestApplication_classLoader);
- parsingPackage
- .setUiOptions(sa.getInt(R.styleable.AndroidManifestApplication_uiOptions, 0))
- .setClassLoaderName(classLoaderName)
- .setZygotePreloadName(
- sa.getString(R.styleable.AndroidManifestApplication_zygotePreloadName));
-
- if (classLoaderName != null
- && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Invalid class loader name: " + classLoaderName
- );
- }
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- final int innerDepth = parser.getDepth();
- 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) {
- continue;
- }
-
- String tagName = parser.getName();
- switch (tagName) {
- case "activity":
- ComponentParseUtils.ParsedActivity activity =
- ComponentParseUtils.parseActivity(separateProcesses,
- parsingPackage,
- res, parser, flags,
- outError, false,
- parsingPackage.isBaseHardwareAccelerated());
- if (activity == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- hasActivityOrder |= (activity.order != 0);
- parsingPackage.addActivity(activity);
- break;
- case "receiver":
- activity = ComponentParseUtils.parseActivity(separateProcesses,
- parsingPackage,
- res, parser,
- flags, outError,
- true, false);
- if (activity == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- hasReceiverOrder |= (activity.order != 0);
- parsingPackage.addReceiver(activity);
- break;
- case "service":
- ComponentParseUtils.ParsedService s = ComponentParseUtils.parseService(
- separateProcesses,
- parsingPackage,
- res, parser, flags,
- outError);
- if (s == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- hasServiceOrder |= (s.order != 0);
- parsingPackage.addService(s);
- break;
- case "provider":
- ComponentParseUtils.ParsedProvider p = ComponentParseUtils.parseProvider(
- separateProcesses,
- parsingPackage,
- res, parser, flags,
- outError
- );
- if (p == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addProvider(p);
- break;
- case "activity-alias":
- activity = ComponentParseUtils.parseActivityAlias(
- parsingPackage,
- res,
- parser,
- outError
- );
- if (activity == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- hasActivityOrder |= (activity.order != 0);
- parsingPackage.addActivity(activity);
- break;
- case "meta-data":
- // note: application meta-data is stored off to the side, so it can
- // remain null in the primary copy (we like to avoid extra copies because
- // it can be large)
- Bundle appMetaData = parseMetaData(parsingPackage, res, parser,
- parsingPackage.getAppMetaData(),
- outError);
- if (appMetaData == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.setAppMetaData(appMetaData);
- break;
- case "static-library":
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestStaticLibrary);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- String lname = sa.getNonResourceString(
- R.styleable.AndroidManifestStaticLibrary_name);
- final int version = sa.getInt(
- R.styleable.AndroidManifestStaticLibrary_version, -1);
- final int versionMajor = sa.getInt(
- R.styleable.AndroidManifestStaticLibrary_versionMajor,
- 0);
-
- sa.recycle();
-
- // Since the app canot run without a static lib - fail if malformed
- if (lname == null || version < 0) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad static-library declaration name: " + lname
- + " version: " + version
- );
- }
-
- if (parsingPackage.getSharedUserId() != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
- "sharedUserId not allowed in static shared library"
- );
- }
-
- if (parsingPackage.getStaticSharedLibName() != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Multiple static-shared libs for package " + pkgName
- );
- }
-
- parsingPackage.setStaticSharedLibName(lname.intern());
- if (version >= 0) {
- parsingPackage.setStaticSharedLibVersion(
- PackageInfo.composeLongVersionCode(versionMajor, version));
- } else {
- parsingPackage.setStaticSharedLibVersion(version);
- }
- parsingPackage.setStaticSharedLibrary(true);
-
- XmlUtils.skipCurrentTag(parser);
-
- break;
- case "library":
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestLibrary);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- lname = sa.getNonResourceString(
- R.styleable.AndroidManifestLibrary_name);
-
- sa.recycle();
-
- if (lname != null) {
- lname = lname.intern();
- if (!ArrayUtils.contains(parsingPackage.getLibraryNames(), lname)) {
- parsingPackage.addLibraryName(lname);
- }
- }
-
- XmlUtils.skipCurrentTag(parser);
-
- break;
- case "uses-static-library":
- ParseResult parseResult = parseUsesStaticLibrary(parseInput, parsingPackage,
- res, parser);
- if (!parseResult.isSuccess()) {
- return parseResult;
- }
- break;
- case "uses-library":
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestUsesLibrary);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- lname = sa.getNonResourceString(
- R.styleable.AndroidManifestUsesLibrary_name);
- boolean req = sa.getBoolean(
- R.styleable.AndroidManifestUsesLibrary_required,
- true);
-
- sa.recycle();
-
- if (lname != null) {
- lname = lname.intern();
- if (req) {
- parsingPackage.addUsesLibrary(lname);
- } else {
- parsingPackage.addUsesOptionalLibrary(lname);
- }
- }
-
- XmlUtils.skipCurrentTag(parser);
-
- break;
- case "processes":
- ArrayMap<String, ComponentParseUtils.ParsedProcess> processes =
- ComponentParseUtils.parseProcesses(separateProcesses,
- parsingPackage,
- res, parser, flags,
- outError);
- if (processes == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.setProcesses(processes);
- break;
- case "uses-package":
- // Dependencies for app installers; we don't currently try to
- // enforce this.
- XmlUtils.skipCurrentTag(parser);
- break;
- case "profileable":
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestProfileable);
- if (sa.getBoolean(
- R.styleable.AndroidManifestProfileable_shell, false)) {
- parsingPackage.setProfileableByShell(true);
- }
- XmlUtils.skipCurrentTag(parser);
- break;
- default:
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <application>: " + tagName
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad element under <application>: " + tagName
- );
- }
- }
- }
-
- if (TextUtils.isEmpty(parsingPackage.getStaticSharedLibName())) {
- // Add a hidden app detail activity to normal apps which forwards user to App Details
- // page.
- ComponentParseUtils.ParsedActivity a = generateAppDetailsHiddenActivity(
- parsingPackage,
- outError
- );
- // Ignore errors here
- parsingPackage.addActivity(a);
- }
-
- if (hasActivityOrder) {
- parsingPackage.sortActivities();
- }
- if (hasReceiverOrder) {
- parsingPackage.sortReceivers();
- }
- if (hasServiceOrder) {
- parsingPackage.sortServices();
- }
- // 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(parsingPackage);
- setMinAspectRatio(parsingPackage, callback);
-
- parsingPackage.setHasDomainUrls(hasDomainURLs(parsingPackage));
-
- return parseInput.success(parsingPackage);
- }
-
- private static ParseResult parseUsesStaticLibrary(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestUsesStaticLibrary);
-
- // Note: don't allow this value to be a reference to a resource that may change.
- String lname = sa.getNonResourceString(
- R.styleable.AndroidManifestUsesLibrary_name);
- final int version = sa.getInt(
- R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
- String certSha256Digest = sa.getNonResourceString(com.android.internal.R.styleable
- .AndroidManifestUsesStaticLibrary_certDigest);
- sa.recycle();
-
- // Since an APK providing a static shared lib can only provide the lib - fail if malformed
- if (lname == null || version < 0 || certSha256Digest == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad uses-static-library declaration name: " + lname + " version: "
- + version + " certDigest" + certSha256Digest
- );
- }
-
- // Can depend only on one version of the same library
- List<String> usesStaticLibraries = parsingPackage.getUsesStaticLibraries();
- if (usesStaticLibraries != null && usesStaticLibraries.contains(lname)) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Depending on multiple versions of static library " + lname
- );
- }
-
- lname = lname.intern();
- // We allow ":" delimiters in the SHA declaration as this is the format
- // emitted by the certtool making it easy for developers to copy/paste.
- certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
-
- // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
- String[] additionalCertSha256Digests = EmptyArray.STRING;
- if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
- additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError);
- if (additionalCertSha256Digests == null || outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
- } else {
- XmlUtils.skipCurrentTag(parser);
- }
-
- final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
- certSha256Digests[0] = certSha256Digest;
- System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
- 1, additionalCertSha256Digests.length);
-
- parsingPackage.addUsesStaticLibrary(lname)
- .addUsesStaticLibraryVersion(version)
- .addUsesStaticLibraryCertDigests(certSha256Digests);
-
- return parseInput.success(parsingPackage);
- }
-
- private static String[] parseAdditionalCertificates(
- Resources resources,
- XmlResourceParser parser,
- String[] outError
- ) throws XmlPullParserException, IOException {
- String[] certSha256Digests = EmptyArray.STRING;
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- final String nodeName = parser.getName();
- if (nodeName.equals("additional-certificate")) {
- final TypedArray sa = resources.obtainAttributes(parser, com.android.internal.
- R.styleable.AndroidManifestAdditionalCertificate);
- String certSha256Digest = sa.getNonResourceString(com.android.internal.
- R.styleable.AndroidManifestAdditionalCertificate_certDigest);
- sa.recycle();
-
- if (TextUtils.isEmpty(certSha256Digest)) {
- outError[0] = "Bad additional-certificate declaration with empty"
- + " certDigest:" + certSha256Digest;
- XmlUtils.skipCurrentTag(parser);
- sa.recycle();
- return null;
- }
-
- // We allow ":" delimiters in the SHA declaration as this is the format
- // emitted by the certtool making it easy for developers to copy/paste.
- certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
- certSha256Digests = ArrayUtils.appendElement(String.class,
- certSha256Digests, certSha256Digest);
- } else {
- XmlUtils.skipCurrentTag(parser);
- }
- }
-
- return certSha256Digests;
- }
-
- /**
- * Generate activity object that forwards user to App Details page automatically.
- * This activity should be invisible to user and user should not know or see it.
- *
- * @hide
- */
- @NonNull
- private static ComponentParseUtils.ParsedActivity generateAppDetailsHiddenActivity(
- ParsingPackage parsingPackage,
- String[] outError
- ) {
- String packageName = parsingPackage.getPackageName();
- String processName = parsingPackage.getProcessName();
- boolean hardwareAccelerated = parsingPackage.isBaseHardwareAccelerated();
- int uiOptions = parsingPackage.getUiOptions();
-
- // Build custom App Details activity info instead of parsing it from xml
- ComponentParseUtils.ParsedActivity activity = new ComponentParseUtils.ParsedActivity();
- activity.setPackageName(packageName);
-
- activity.theme = android.R.style.Theme_NoDisplay;
- activity.exported = true;
- activity.className = PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME;
- activity.setProcessName(processName, processName);
- activity.uiOptions = uiOptions;
- activity.taskAffinity = PackageParser.buildTaskAffinityName(packageName,
- packageName,
- ":app_details", outError);
- activity.enabled = true;
- activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
- activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
- activity.configChanges = PackageParser.getActivityConfigChanges(0, 0);
- activity.softInputMode = 0;
- activity.persistableMode = ActivityInfo.PERSIST_NEVER;
- activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
- activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
- activity.lockTaskLaunchMode = 0;
- activity.directBootAware = false;
- activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
- activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
- activity.preferMinimalPostProcessing = ActivityInfo.MINIMAL_POST_PROCESSING_DEFAULT;
- if (hardwareAccelerated) {
- activity.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
- }
-
- return activity;
- }
-
- /**
- * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
- */
- private static boolean hasDomainURLs(
- ParsingPackage parsingPackage) {
- final List<ComponentParseUtils.ParsedActivity> activities = parsingPackage.getActivities();
- final int countActivities = activities.size();
- for (int n = 0; n < countActivities; n++) {
- ComponentParseUtils.ParsedActivity activity = activities.get(n);
- List<ComponentParseUtils.ParsedActivityIntentInfo> filters = activity.intents;
- if (filters == null) continue;
- final int countFilters = filters.size();
- for (int m = 0; m < countFilters; m++) {
- ComponentParseUtils.ParsedActivityIntentInfo aii = filters.get(m);
- if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
- if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
- if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
- aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Sets the max aspect ratio of every child activity that doesn't already have an aspect
- * ratio set.
- */
- private static void setMaxAspectRatio(
- ParsingPackage parsingPackage) {
- // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
- // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
- float maxAspectRatio = parsingPackage.getTargetSdkVersion() < O
- ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
-
- float packageMaxAspectRatio = parsingPackage.getMaxAspectRatio();
- if (packageMaxAspectRatio != 0) {
- // Use the application max aspect ration as default if set.
- maxAspectRatio = packageMaxAspectRatio;
- } else {
- Bundle appMetaData = parsingPackage.getAppMetaData();
- if (appMetaData != null && appMetaData.containsKey(
- PackageParser.METADATA_MAX_ASPECT_RATIO)) {
- maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
- maxAspectRatio);
- }
- }
-
- if (parsingPackage.getActivities() != null) {
- for (ComponentParseUtils.ParsedActivity activity : parsingPackage.getActivities()) {
- // If the max aspect ratio for the activity has already been set, skip.
- if (activity.hasMaxAspectRatio()) {
- continue;
- }
-
- // By default we prefer to use a values defined on the activity directly than values
- // defined on the application. We do not check the styled attributes on the activity
- // as it would have already been set when we processed the activity. We wait to
- // process the meta data here since this method is called at the end of processing
- // the application and all meta data is guaranteed.
- final float activityAspectRatio = activity.metaData != null
- ? activity.metaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
- maxAspectRatio)
- : maxAspectRatio;
-
- activity.setMaxAspectRatio(activity.resizeMode, activityAspectRatio);
- }
- }
- }
-
- /**
- * Sets the min aspect ratio of every child activity that doesn't already have an aspect
- * ratio set.
- */
- private static void setMinAspectRatio(
- ParsingPackage parsingPackage,
- PackageParser.Callback callback
- ) {
- final float minAspectRatio;
- float packageMinAspectRatio = parsingPackage.getMinAspectRatio();
- if (packageMinAspectRatio != 0) {
- // Use the application max aspect ration as default if set.
- minAspectRatio = packageMinAspectRatio;
- } else {
- // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
- // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
- // except for watches which always supported 1:1.
- minAspectRatio = parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q
- ? 0
- : (callback != null && callback.hasFeature(FEATURE_WATCH))
- ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
- : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
- }
-
- if (parsingPackage.getActivities() != null) {
- for (ComponentParseUtils.ParsedActivity activity : parsingPackage.getActivities()) {
- if (activity.hasMinAspectRatio()) {
- continue;
- }
- activity.setMinAspectRatio(activity.resizeMode, minAspectRatio);
- }
- }
- }
-
- private static ParseResult parseOverlay(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
-
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
- String target = sa.getString(
- R.styleable.AndroidManifestResourceOverlay_targetPackage);
- String targetName = sa.getString(
- R.styleable.AndroidManifestResourceOverlay_targetName);
- String category = sa.getString(
- R.styleable.AndroidManifestResourceOverlay_category);
- int priority = sa.getInt(R.styleable.AndroidManifestResourceOverlay_priority,
- 0);
- boolean isStatic = sa.getBoolean(
- R.styleable.AndroidManifestResourceOverlay_isStatic, false);
-
- if (target == null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "<overlay> does not specify a target package"
- );
- }
-
- if (priority < 0 || priority > 9999) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "<overlay> priority must be between 0 and 9999"
- );
- }
-
- // check to see if overlay should be excluded based on system property condition
- String propName = sa.getString(
- R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
- String propValue = sa.getString(
- R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
- if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
- Slog.i(TAG, "Skipping target and overlay pair " + target + " and "
- + parsingPackage.getBaseCodePath()
- + ": overlay ignored due to required system property: "
- + propName + " with value: " + propValue);
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Skipping target and overlay pair " + target + " and "
- + parsingPackage.getBaseCodePath()
- + ": overlay ignored due to required system property: "
- + propName + " with value: " + propValue
- );
- }
-
- parsingPackage
- .setIsOverlay(true)
- .setOverlayTarget(target)
- .setOverlayTargetName(targetName)
- .setOverlayCategory(category)
- .setOverlayPriority(priority)
- .setOverlayIsStatic(isStatic);
-
- sa.recycle();
-
- XmlUtils.skipCurrentTag(parser);
- return parseInput.success(parsingPackage);
- }
-
- private static boolean parseProtectedBroadcast(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestProtectedBroadcast);
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- String name = sa.getNonResourceString(R.styleable.AndroidManifestProtectedBroadcast_name);
-
- sa.recycle();
-
- if (name != null) {
- parsingPackage.addProtectedBroadcast(name);
- }
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static boolean parseSupportScreens(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestSupportsScreens);
-
- int requiresSmallestWidthDp = sa.getInteger(
- R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
- 0);
- int compatibleWidthLimitDp = sa.getInteger(
- R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
- 0);
- int largestWidthLimitDp = sa.getInteger(
- R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
- 0);
-
- // This is a trick to get a boolean and still able to detect
- // if a value was actually set.
- parsingPackage
- .setSupportsSmallScreens(
- sa.getInteger(R.styleable.AndroidManifestSupportsScreens_smallScreens, 1))
- .setSupportsNormalScreens(
- sa.getInteger(R.styleable.AndroidManifestSupportsScreens_normalScreens, 1))
- .setSupportsLargeScreens(
- sa.getInteger(R.styleable.AndroidManifestSupportsScreens_largeScreens, 1))
- .setSupportsXLargeScreens(
- sa.getInteger(R.styleable.AndroidManifestSupportsScreens_xlargeScreens, 1))
- .setResizeable(
- sa.getInteger(R.styleable.AndroidManifestSupportsScreens_resizeable, 1))
- .setAnyDensity(
- sa.getInteger(R.styleable.AndroidManifestSupportsScreens_anyDensity, 1))
- .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
- .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
- .setLargestWidthLimitDp(largestWidthLimitDp);
-
- sa.recycle();
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static ParseResult parseInstrumentation(
- ParseInput parseInput,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws XmlPullParserException, IOException {
- // TODO(b/135203078): Remove, replace with ParseResult
- String[] outError = new String[1];
-
- ComponentParseUtils.ParsedInstrumentation parsedInstrumentation =
- ComponentParseUtils.parseInstrumentation(parsingPackage,
- res, parser, outError);
-
- if (parsedInstrumentation == null || outError[0] != null) {
- return parseInput.error(
- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- outError[0]
- );
- }
-
- parsingPackage.addInstrumentation(parsedInstrumentation);
-
- return parseInput.success(parsingPackage);
- }
-
- private static boolean parseOriginalPackage(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestOriginalPackage);
-
- String orig = sa.getNonConfigurationString(
- R.styleable.AndroidManifestOriginalPackage_name,
- 0);
- if (!parsingPackage.getPackageName().equals(orig)) {
- if (parsingPackage.getOriginalPackages() == null) {
- parsingPackage.setRealPackage(parsingPackage.getPackageName());
- }
- parsingPackage.addOriginalPackage(orig);
- }
-
- sa.recycle();
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static boolean parseAdoptPermissions(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestOriginalPackage);
-
- String name = sa.getNonConfigurationString(
- R.styleable.AndroidManifestOriginalPackage_name,
- 0);
-
- sa.recycle();
-
- if (name != null) {
- parsingPackage.addAdoptPermission(name);
- }
-
- XmlUtils.skipCurrentTag(parser);
- return true;
- }
-
- private static void convertNewPermissions(
- ParsingPackage packageToParse) {
- final int NP = PackageParser.NEW_PERMISSIONS.length;
- StringBuilder newPermsMsg = null;
- for (int ip = 0; ip < NP; ip++) {
- final PackageParser.NewPermissionInfo npi
- = PackageParser.NEW_PERMISSIONS[ip];
- if (packageToParse.getTargetSdkVersion() >= npi.sdkVersion) {
- break;
- }
- if (!packageToParse.getRequestedPermissions().contains(npi.name)) {
- if (newPermsMsg == null) {
- newPermsMsg = new StringBuilder(128);
- newPermsMsg.append(packageToParse.getPackageName());
- newPermsMsg.append(": compat added ");
- } else {
- newPermsMsg.append(' ');
- }
- newPermsMsg.append(npi.name);
- packageToParse.addRequestedPermission(npi.name);
- packageToParse.addImplicitPermission(npi.name);
- }
- }
- if (newPermsMsg != null) {
- Slog.i(TAG, newPermsMsg.toString());
- }
- }
-
- private static void convertSplitPermissions(ParsingPackage packageToParse) {
- List<SplitPermissionInfoParcelable> splitPermissions;
-
- try {
- splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- final int listSize = splitPermissions.size();
- for (int is = 0; is < listSize; is++) {
- final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
- List<String> requestedPermissions = packageToParse.getRequestedPermissions();
- if (packageToParse.getTargetSdkVersion() >= spi.getTargetSdk()
- || !requestedPermissions.contains(spi.getSplitPermission())) {
- continue;
- }
- final List<String> newPerms = spi.getNewPermissions();
- for (int in = 0; in < newPerms.size(); in++) {
- final String perm = newPerms.get(in);
- if (!requestedPermissions.contains(perm)) {
- packageToParse.addRequestedPermission(perm);
- packageToParse.addImplicitPermission(perm);
- }
- }
- }
- }
-
- private static boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {
- if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
- if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
- // malformed condition - incomplete
- Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName
- + "=" + propValue + "' - require both requiredSystemPropertyName"
- + " AND requiredSystemPropertyValue to be specified.");
- return false;
- }
- // no valid condition set - so no exclusion criteria, overlay will be included.
- return true;
- }
-
- // check property value - make sure it is both set and equal to expected value
- final String currValue = SystemProperties.get(propName);
- return (currValue != null && currValue.equals(propValue));
- }
-
- /**
- * This is a pre-density application which will get scaled - instead of being pixel perfect.
- * This type of application is not resizable.
- *
- * @param parsingPackage The package which needs to be marked as unresizable.
- */
- private static void adjustPackageToBeUnresizeableAndUnpipable(
- ParsingPackage parsingPackage) {
- if (parsingPackage.getActivities() != null) {
- for (ComponentParseUtils.ParsedActivity a : parsingPackage.getActivities()) {
- a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
- a.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE;
- }
- }
- }
-
- private static String validateName(String name, boolean requireSeparator,
- boolean requireFilename) {
- final int N = name.length();
- boolean hasSep = false;
- boolean front = true;
- for (int i = 0; i < N; i++) {
- final char c = name.charAt(i);
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
- front = false;
- continue;
- }
- if (!front) {
- if ((c >= '0' && c <= '9') || c == '_') {
- continue;
- }
- }
- if (c == '.') {
- hasSep = true;
- front = true;
- continue;
- }
- return "bad character '" + c + "'";
- }
- if (requireFilename && !FileUtils.isValidExtFilename(name)) {
- return "Invalid filename";
- }
- return hasSep || !requireSeparator
- ? null : "must have at least one '.' separator";
- }
-
- public static Bundle parseMetaData(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser, Bundle data, String[] outError)
- throws XmlPullParserException, IOException {
-
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestMetaData);
-
- if (data == null) {
- data = new Bundle();
- }
-
- String name = sa.getNonConfigurationString(
- R.styleable.AndroidManifestMetaData_name, 0);
- if (name == null) {
- outError[0] = "<meta-data> requires an android:name attribute";
- sa.recycle();
- return null;
- }
-
- name = name.intern();
-
- TypedValue v = sa.peekValue(
- R.styleable.AndroidManifestMetaData_resource);
- if (v != null && v.resourceId != 0) {
- //Slog.i(TAG, "Meta data ref " + name + ": " + v);
- data.putInt(name, v.resourceId);
- } else {
- v = sa.peekValue(
- R.styleable.AndroidManifestMetaData_value);
- //Slog.i(TAG, "Meta data " + name + ": " + v);
- if (v != null) {
- if (v.type == TypedValue.TYPE_STRING) {
- CharSequence cs = v.coerceToString();
- data.putString(name, cs != null ? cs.toString() : null);
- } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
- data.putBoolean(name, v.data != 0);
- } else if (v.type >= TypedValue.TYPE_FIRST_INT
- && v.type <= TypedValue.TYPE_LAST_INT) {
- data.putInt(name, v.data);
- } else if (v.type == TypedValue.TYPE_FLOAT) {
- data.putFloat(name, v.getFloat());
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG,
- "<meta-data> only supports string, integer, float, color, "
- + "boolean, and resource reference types: "
- + parser.getName() + " at "
- + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- } else {
- outError[0] =
- "<meta-data> only supports string, integer, float, color, "
- + "boolean, and resource reference types";
- data = null;
- }
- }
- } else {
- outError[0] = "<meta-data> requires an android:value or android:resource attribute";
- data = null;
- }
- }
-
- sa.recycle();
-
- XmlUtils.skipCurrentTag(parser);
-
- return data;
- }
-
- /**
- * Collect certificates from all the APKs described in the given package,
- * populating {@link AndroidPackageWrite#setSigningDetails(SigningDetails)}. Also asserts that
- * all APK contents are signed correctly and consistently.
- */
- public static void collectCertificates(AndroidPackage pkg, boolean skipVerify)
- throws PackageParserException {
- pkg.mutate().setSigningDetails(SigningDetails.UNKNOWN);
-
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- try {
- pkg.mutate().setSigningDetails(collectCertificates(
- pkg.getBaseCodePath(),
- skipVerify,
- pkg.isStaticSharedLibrary(),
- pkg.getSigningDetails(),
- pkg.getTargetSdkVersion()
- ));
-
- String[] splitCodePaths = pkg.getSplitCodePaths();
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- pkg.mutate().setSigningDetails(collectCertificates(
- splitCodePaths[i],
- skipVerify,
- pkg.isStaticSharedLibrary(),
- pkg.getSigningDetails(),
- pkg.getTargetSdkVersion()
- ));
- }
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
- public static SigningDetails collectCertificates(
- String baseCodePath,
- boolean skipVerify,
- boolean isStaticSharedLibrary,
- @NonNull SigningDetails existingSigningDetails,
- int targetSdk
- ) throws PackageParserException {
- int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
- targetSdk);
- if (isStaticSharedLibrary) {
- // must use v2 signing scheme
- minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
- }
- SigningDetails verified;
- if (skipVerify) {
- // systemDir APKs are already trusted, save time by not verifying
- verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
- baseCodePath, minSignatureScheme);
- } else {
- verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
- }
-
- // Verify that entries are signed consistently with the first pkg
- // we encountered. Note that for splits, certificates may have
- // already been populated during an earlier parse of a base APK.
- if (existingSigningDetails == SigningDetails.UNKNOWN) {
- return verified;
- } else {
- if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
- throw new PackageParserException(
- INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
- baseCodePath + " has mismatched certificates");
- }
-
- return existingSigningDetails;
- }
- }
-
- @Nullable
- public static String buildClassName(String pkg, CharSequence clsSeq) {
- if (clsSeq == null || clsSeq.length() <= 0) {
- return null;
- }
- String cls = clsSeq.toString();
- char c = cls.charAt(0);
- if (c == '.') {
- return pkg + cls;
- }
- if (cls.indexOf('.') < 0) {
- StringBuilder b = new StringBuilder(pkg);
- b.append('.');
- b.append(cls);
- return b.toString();
- }
- return cls;
- }
-
- public interface ParseInput {
- ParseResult success(ParsingPackage result);
-
- ParseResult error(int parseError);
-
- ParseResult error(int parseError, String errorMessage);
- }
-
- public static class ParseResult implements ParseInput {
-
- private static final boolean DEBUG_FILL_STACK_TRACE = false;
-
- private ParsingPackage result;
-
- private int parseError;
- private String errorMessage;
-
- public ParseInput reset() {
- this.result = null;
- this.parseError = PackageManager.INSTALL_SUCCEEDED;
- this.errorMessage = null;
- return this;
- }
-
- @Override
- public ParseResult success(ParsingPackage result) {
- if (parseError != PackageManager.INSTALL_SUCCEEDED || errorMessage != null) {
- throw new IllegalStateException("Cannot set to success after set to error");
- }
- this.result = result;
- return this;
- }
-
- @Override
- public ParseResult error(int parseError) {
- return error(parseError, null);
- }
-
- @Override
- public ParseResult error(int parseError, String errorMessage) {
- this.parseError = parseError;
- this.errorMessage = errorMessage;
-
- if (DEBUG_FILL_STACK_TRACE) {
- this.errorMessage += Arrays.toString(new Exception().getStackTrace());
- }
-
- return this;
- }
-
- public ParsingPackage getResultAndNull() {
- ParsingPackage result = this.result;
- this.result = null;
- return result;
- }
-
- public boolean isSuccess() {
- return parseError == PackageManager.INSTALL_SUCCEEDED;
- }
-
- public int getParseError() {
- return parseError;
- }
-
- public String getErrorMessage() {
- return errorMessage;
- }
- }
-}
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
deleted file mode 100644
index 6852a8c..0000000
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ /dev/null
@@ -1,3793 +0,0 @@
-/*
- * Copyright (C) 2019 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.parsing;
-
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.StringRes;
-import android.app.ActivityTaskManager;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PathPermission;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.PatternMatcher;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.TypedValue;
-import android.view.Gravity;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DataClass;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * TODO(b/135203078): Move the inner classes out to separate files.
- * TODO(b/135203078): Expose inner classes as immutable through interface methods.
- *
- * @hide
- */
-public class ComponentParseUtils {
-
- private static final String TAG = ApkParseUtils.TAG;
-
- // TODO(b/135203078): None of this class's subclasses do anything. Remove in favor of base?
- public static class ParsedIntentInfo extends IntentFilter {
-
- /**
- * <p>
- * Implementation note: The serialized form for the intent list also contains the name
- * of the concrete class that's stored in the list, and assumes that every element of the
- * list is of the same type. This is very similar to the original parcelable mechanism.
- * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
- * and is public API. It also declares Parcelable related methods as final which means
- * we can't extend them. The approach of using composition instead of inheritance leads to
- * a large set of cascading changes in the PackageManagerService, which seem undesirable.
- *
- * <p>
- * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
- * to make sure their owner fields are consistent. See {@code fixupOwner}.
- */
- public static void writeIntentsList(List<? extends ParsedIntentInfo> list, Parcel out,
- int flags) {
- if (list == null) {
- out.writeInt(-1);
- return;
- }
-
- final int size = list.size();
- out.writeInt(size);
-
- // Don't bother writing the component name if the list is empty.
- if (size > 0) {
- ParsedIntentInfo info = list.get(0);
- out.writeString(info.getClass().getName());
-
- for (int i = 0; i < size; i++) {
- list.get(i).writeIntentInfoToParcel(out, flags);
- }
- }
- }
-
- public static <T extends ParsedIntentInfo> ArrayList<T> createIntentsList(Parcel in) {
- int size = in.readInt();
- if (size == -1) {
- return null;
- }
-
- if (size == 0) {
- return new ArrayList<>(0);
- }
-
- String className = in.readString();
- final ArrayList<T> intentsList;
- try {
- final Class<T> cls = (Class<T>) Class.forName(className);
- final Constructor<T> cons = cls.getConstructor(Parcel.class);
-
- intentsList = new ArrayList<>(size);
- for (int i = 0; i < size; ++i) {
- intentsList.add(cons.newInstance(in));
- }
- } catch (ReflectiveOperationException ree) {
- throw new AssertionError("Unable to construct intent list for: "
- + className, ree);
- }
-
- return intentsList;
- }
-
- protected String packageName;
- protected final String className;
-
- public boolean hasDefault;
- public int labelRes;
- public CharSequence nonLocalizedLabel;
- public int icon;
-
- protected List<String> rawDataTypes;
-
- public void addRawDataType(String dataType) throws MalformedMimeTypeException {
- if (rawDataTypes == null) {
- rawDataTypes = new ArrayList<>();
- }
-
- rawDataTypes.add(dataType);
- addDataType(dataType);
- }
-
- public ParsedIntentInfo(String packageName, String className) {
- this.packageName = packageName;
- this.className = className;
- }
-
- public ParsedIntentInfo(Parcel in) {
- super(in);
- packageName = in.readString();
- className = in.readString();
- hasDefault = (in.readInt() == 1);
- labelRes = in.readInt();
- nonLocalizedLabel = in.readCharSequence();
- icon = in.readInt();
- }
-
- public void writeIntentInfoToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(packageName);
- dest.writeString(className);
- dest.writeInt(hasDefault ? 1 : 0);
- dest.writeInt(labelRes);
- dest.writeCharSequence(nonLocalizedLabel);
- dest.writeInt(icon);
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- public String getClassName() {
- return className;
- }
- }
-
- public static class ParsedActivityIntentInfo extends ParsedIntentInfo {
-
- public ParsedActivityIntentInfo(String packageName, String className) {
- super(packageName, className);
- }
-
- public ParsedActivityIntentInfo(Parcel in) {
- super(in);
- }
-
- public static final Creator<ParsedActivityIntentInfo> CREATOR =
- new Creator<ParsedActivityIntentInfo>() {
- @Override
- public ParsedActivityIntentInfo createFromParcel(Parcel source) {
- return new ParsedActivityIntentInfo(source);
- }
-
- @Override
- public ParsedActivityIntentInfo[] newArray(int size) {
- return new ParsedActivityIntentInfo[size];
- }
- };
- }
-
- public static class ParsedServiceIntentInfo extends ParsedIntentInfo {
-
- public ParsedServiceIntentInfo(String packageName, String className) {
- super(packageName, className);
- }
-
- public ParsedServiceIntentInfo(Parcel in) {
- super(in);
- }
-
- public static final Creator<ParsedServiceIntentInfo> CREATOR =
- new Creator<ParsedServiceIntentInfo>() {
- @Override
- public ParsedServiceIntentInfo createFromParcel(Parcel source) {
- return new ParsedServiceIntentInfo(source);
- }
-
- @Override
- public ParsedServiceIntentInfo[] newArray(int size) {
- return new ParsedServiceIntentInfo[size];
- }
- };
- }
-
- public static class ParsedProviderIntentInfo extends ParsedIntentInfo {
-
- public ParsedProviderIntentInfo(String packageName, String className) {
- super(packageName, className);
- }
-
- public ParsedProviderIntentInfo(Parcel in) {
- super(in);
- }
-
- public static final Creator<ParsedProviderIntentInfo> CREATOR =
- new Creator<ParsedProviderIntentInfo>() {
- @Override
- public ParsedProviderIntentInfo createFromParcel(Parcel source) {
- return new ParsedProviderIntentInfo(source);
- }
-
- @Override
- public ParsedProviderIntentInfo[] newArray(int size) {
- return new ParsedProviderIntentInfo[size];
- }
- };
- }
-
- public static class ParsedQueriesIntentInfo extends ParsedIntentInfo {
-
- public ParsedQueriesIntentInfo(String packageName, String className) {
- super(packageName, className);
- }
-
- public ParsedQueriesIntentInfo(Parcel in) {
- super(in);
- }
-
- public static final Creator<ParsedQueriesIntentInfo> CREATOR =
- new Creator<ParsedQueriesIntentInfo>() {
- @Override
- public ParsedQueriesIntentInfo createFromParcel(Parcel source) {
- return new ParsedQueriesIntentInfo(source);
- }
-
- @Override
- public ParsedQueriesIntentInfo[] newArray(int size) {
- return new ParsedQueriesIntentInfo[size];
- }
- };
- }
-
- public static class ParsedComponent<IntentInfoType extends ParsedIntentInfo> implements
- Parcelable {
-
- // TODO(b/135203078): Replace with "name", as not all usages are an actual class
- public String className;
- public int icon;
- public int labelRes;
- public CharSequence nonLocalizedLabel;
- public int logo;
- public int banner;
-
- public int descriptionRes;
-
- // TODO(b/135203078): Make subclass that contains these fields only for the necessary
- // subtypes
- protected boolean enabled = true;
- protected boolean directBootAware;
- public int flags;
-
- private String packageName;
- private String splitName;
-
- // TODO(b/135203078): Make nullable
- public List<IntentInfoType> intents = new ArrayList<>();
-
- private transient ComponentName componentName;
-
- protected Bundle metaData;
-
- public void setSplitName(String splitName) {
- this.splitName = splitName;
- }
-
- public String getSplitName() {
- return splitName;
- }
-
- @CallSuper
- public void setPackageName(String packageName) {
- this.packageName = packageName;
- this.componentName = null;
- }
-
- void setPackageNameInternal(String packageName) {
- this.packageName = packageName;
- this.componentName = null;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public String getPackageName() {
- return packageName;
- }
-
- public final boolean isDirectBootAware() {
- return directBootAware;
- }
-
- public final boolean isEnabled() {
- return enabled;
- }
-
- public final String getName() {
- return className;
- }
-
- public final Bundle getMetaData() {
- return metaData;
- }
-
- @UnsupportedAppUsage
- public ComponentName getComponentName() {
- if (componentName != null) {
- return componentName;
- }
- if (className != null) {
- componentName = new ComponentName(getPackageName(),
- className);
- }
- return componentName;
- }
-
- public void setFrom(ParsedComponent other) {
- this.metaData = other.metaData;
- this.className = other.className;
- this.icon = other.icon;
- this.labelRes = other.labelRes;
- this.nonLocalizedLabel = other.nonLocalizedLabel;
- this.logo = other.logo;
- this.banner = other.banner;
-
- this.descriptionRes = other.descriptionRes;
-
- this.enabled = other.enabled;
- this.directBootAware = other.directBootAware;
- this.flags = other.flags;
-
- this.setPackageName(other.packageName);
- this.setSplitName(other.getSplitName());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(this.className);
- dest.writeInt(this.icon);
- dest.writeInt(this.labelRes);
- dest.writeCharSequence(this.nonLocalizedLabel);
- dest.writeInt(this.logo);
- dest.writeInt(this.banner);
- dest.writeInt(this.descriptionRes);
- dest.writeBoolean(this.enabled);
- dest.writeBoolean(this.directBootAware);
- dest.writeInt(this.flags);
- dest.writeString(this.packageName);
- dest.writeString(this.splitName);
- ParsedIntentInfo.writeIntentsList(this.intents, dest, flags);
- dest.writeBundle(this.metaData);
- }
-
- public ParsedComponent() {
- }
-
- protected ParsedComponent(Parcel in) {
- // We use the boot classloader for all classes that we load.
- final ClassLoader boot = Object.class.getClassLoader();
- this.className = in.readString();
- this.icon = in.readInt();
- this.labelRes = in.readInt();
- this.nonLocalizedLabel = in.readCharSequence();
- this.logo = in.readInt();
- this.banner = in.readInt();
- this.descriptionRes = in.readInt();
- this.enabled = in.readByte() != 0;
- this.directBootAware = in.readByte() != 0;
- this.flags = in.readInt();
- this.packageName = in.readString();
- this.splitName = in.readString();
- this.intents = ParsedIntentInfo.createIntentsList(in);
- this.metaData = in.readBundle(boot);
- }
- }
-
- // TODO(b/135203078): Document this. Maybe split out ParsedComponent to be actual components
- // that can have their own processes, rather than something like permission which cannot.
- public static class ParsedMainComponent<IntentInfoType extends ParsedIntentInfo> extends
- ParsedComponent<IntentInfoType> {
-
- private String processName;
- private String permission;
-
- public void setProcessName(String appProcessName, String processName) {
- // TODO(b/135203078): Is this even necessary anymore?
- this.processName = TextUtils.safeIntern(
- processName == null ? appProcessName : processName);
- }
-
- public String getProcessName() {
- return processName;
- }
-
- public void setPermission(String permission) {
- // Empty string must be converted to null
- this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
- }
-
- public String getPermission() {
- return permission;
- }
-
- @Override
- public void setFrom(ParsedComponent other) {
- super.setFrom(other);
- if (other instanceof ParsedMainComponent) {
- ParsedMainComponent otherMainComponent = (ParsedMainComponent) other;
- this.setProcessName(otherMainComponent.getProcessName(),
- otherMainComponent.getProcessName());
- this.setPermission(otherMainComponent.getPermission());
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(this.processName);
- dest.writeString(this.permission);
- }
-
- public ParsedMainComponent() {
- }
-
- protected ParsedMainComponent(Parcel in) {
- super(in);
- this.processName = TextUtils.safeIntern(in.readString());
- this.permission = TextUtils.safeIntern(in.readString());
- }
-
- public static final Creator<ParsedMainComponent> CREATOR =
- new Creator<ParsedMainComponent>() {
- @Override
- public ParsedMainComponent createFromParcel(Parcel source) {
- return new ParsedMainComponent(source);
- }
-
- @Override
- public ParsedMainComponent[] newArray(int size) {
- return new ParsedMainComponent[size];
- }
- };
- }
-
- public static class ParsedActivity extends ParsedMainComponent<ParsedActivityIntentInfo>
- implements Parcelable {
-
- public boolean exported;
- public int theme;
- public int uiOptions;
-
- public String targetActivity;
-
- public String parentActivityName;
- public String taskAffinity;
- public int privateFlags;
-
- public int launchMode;
- public int documentLaunchMode;
- public int maxRecents;
- public int configChanges;
- public int softInputMode;
- public int persistableMode;
- public int lockTaskLaunchMode;
-
- public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- public int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
-
- public float maxAspectRatio;
- public boolean hasMaxAspectRatio;
-
- public float minAspectRatio;
- public boolean hasMinAspectRatio;
-
- public String requestedVrComponent;
- public int rotationAnimation = -1;
- public int colorMode;
- public boolean preferMinimalPostProcessing;
- public int order;
-
- public ActivityInfo.WindowLayout windowLayout;
-
- @Override
- public void setPackageName(String packageName) {
- super.setPackageName(packageName);
- for (ParsedIntentInfo intent : this.intents) {
- intent.packageName = packageName;
- }
- }
-
- public boolean hasMaxAspectRatio() {
- return hasMaxAspectRatio;
- }
-
- public boolean hasMinAspectRatio() {
- return hasMinAspectRatio;
- }
-
- public void setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
- if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
- || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
- // Resizeable activities can be put in any aspect ratio.
- return;
- }
-
- if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
- // Ignore any value lesser than 1.0.
- return;
- }
-
- this.maxAspectRatio = maxAspectRatio;
- hasMaxAspectRatio = true;
- }
-
- public void setMinAspectRatio(int resizeMode, float minAspectRatio) {
- if (resizeMode == RESIZE_MODE_RESIZEABLE
- || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
- // Resizeable activities can be put in any aspect ratio.
- return;
- }
-
- if (minAspectRatio < 1.0f && minAspectRatio != 0) {
- // Ignore any value lesser than 1.0.
- return;
- }
-
- this.minAspectRatio = minAspectRatio;
- hasMinAspectRatio = true;
- }
-
- public void addIntent(ParsedActivityIntentInfo intent) {
- this.intents.add(intent);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeBoolean(this.exported);
- dest.writeInt(this.theme);
- dest.writeInt(this.uiOptions);
- dest.writeString(this.targetActivity);
- dest.writeString(this.parentActivityName);
- dest.writeString(this.taskAffinity);
- dest.writeInt(this.privateFlags);
- dest.writeInt(this.launchMode);
- dest.writeInt(this.documentLaunchMode);
- dest.writeInt(this.maxRecents);
- dest.writeInt(this.configChanges);
- dest.writeInt(this.softInputMode);
- dest.writeInt(this.persistableMode);
- dest.writeInt(this.lockTaskLaunchMode);
- dest.writeInt(this.screenOrientation);
- dest.writeInt(this.resizeMode);
- dest.writeFloat(this.maxAspectRatio);
- dest.writeBoolean(this.hasMaxAspectRatio);
- dest.writeFloat(this.minAspectRatio);
- dest.writeBoolean(this.hasMinAspectRatio);
- dest.writeString(this.requestedVrComponent);
- dest.writeInt(this.rotationAnimation);
- dest.writeInt(this.colorMode);
- dest.writeBoolean(this.preferMinimalPostProcessing);
- dest.writeInt(this.order);
- dest.writeBundle(this.metaData);
-
- if (windowLayout != null) {
- dest.writeInt(1);
- dest.writeInt(windowLayout.width);
- dest.writeFloat(windowLayout.widthFraction);
- dest.writeInt(windowLayout.height);
- dest.writeFloat(windowLayout.heightFraction);
- dest.writeInt(windowLayout.gravity);
- dest.writeInt(windowLayout.minWidth);
- dest.writeInt(windowLayout.minHeight);
- } else {
- dest.writeInt(0);
- }
- }
-
- public ParsedActivity() {
- }
-
- protected ParsedActivity(Parcel in) {
- super(in);
- this.exported = in.readByte() != 0;
- this.theme = in.readInt();
- this.uiOptions = in.readInt();
- this.targetActivity = in.readString();
- this.parentActivityName = in.readString();
- this.taskAffinity = in.readString();
- this.privateFlags = in.readInt();
- this.launchMode = in.readInt();
- this.documentLaunchMode = in.readInt();
- this.maxRecents = in.readInt();
- this.configChanges = in.readInt();
- this.softInputMode = in.readInt();
- this.persistableMode = in.readInt();
- this.lockTaskLaunchMode = in.readInt();
- this.screenOrientation = in.readInt();
- this.resizeMode = in.readInt();
- this.maxAspectRatio = in.readFloat();
- this.hasMaxAspectRatio = in.readByte() != 0;
- this.minAspectRatio = in.readFloat();
- this.hasMinAspectRatio = in.readByte() != 0;
- this.requestedVrComponent = in.readString();
- this.rotationAnimation = in.readInt();
- this.colorMode = in.readInt();
- this.preferMinimalPostProcessing = in.readByte() != 0;
- this.order = in.readInt();
- this.metaData = in.readBundle();
- if (in.readInt() == 1) {
- windowLayout = new ActivityInfo.WindowLayout(in);
- }
- }
-
- public static final Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
- @Override
- public ParsedActivity createFromParcel(Parcel source) {
- return new ParsedActivity(source);
- }
-
- @Override
- public ParsedActivity[] newArray(int size) {
- return new ParsedActivity[size];
- }
- };
- }
-
- public static class ParsedService extends ParsedMainComponent<ParsedServiceIntentInfo> {
-
- public boolean exported;
- public int flags;
- public int foregroundServiceType;
- public int order;
-
- @Override
- public void setPackageName(String packageName) {
- super.setPackageName(packageName);
- for (ParsedIntentInfo intent : this.intents) {
- intent.packageName = packageName;
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeBoolean(this.exported);
- dest.writeBundle(this.metaData);
- dest.writeInt(this.flags);
- dest.writeInt(this.foregroundServiceType);
- dest.writeInt(this.order);
- }
-
- public ParsedService() {
- }
-
- protected ParsedService(Parcel in) {
- super(in);
- this.exported = in.readByte() != 0;
- this.metaData = in.readBundle();
- this.flags = in.readInt();
- this.foregroundServiceType = in.readInt();
- this.order = in.readInt();
- }
-
- public static final Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
- @Override
- public ParsedService createFromParcel(Parcel source) {
- return new ParsedService(source);
- }
-
- @Override
- public ParsedService[] newArray(int size) {
- return new ParsedService[size];
- }
- };
- }
-
- public static class ParsedProvider extends ParsedMainComponent<ParsedProviderIntentInfo> {
-
- protected boolean exported;
- protected int flags;
- protected int order;
- private String authority;
- protected boolean isSyncable;
- private String readPermission;
- private String writePermission;
- protected boolean grantUriPermissions;
- protected boolean forceUriPermissions;
- protected boolean multiProcess;
- protected int initOrder;
- protected PatternMatcher[] uriPermissionPatterns;
- protected PathPermission[] pathPermissions;
-
- public ParsedProvider(ParsedProvider other) {
- this.setFrom(other);
- }
-
- protected void setFrom(ParsedProvider other) {
- super.setFrom(other);
- this.exported = other.exported;
-
- this.intents.clear();
- if (other.intents != null) {
- this.intents.addAll(other.intents);
- }
-
- this.flags = other.flags;
- this.order = other.order;
- this.setAuthority(other.getAuthority());
- this.isSyncable = other.isSyncable;
- this.setReadPermission(other.getReadPermission());
- this.setWritePermission(other.getWritePermission());
- this.grantUriPermissions = other.grantUriPermissions;
- this.forceUriPermissions = other.forceUriPermissions;
- this.multiProcess = other.multiProcess;
- this.initOrder = other.initOrder;
- this.uriPermissionPatterns = other.uriPermissionPatterns;
- this.pathPermissions = other.pathPermissions;
- }
-
- @Override
- public void setPackageName(String packageName) {
- super.setPackageName(packageName);
- for (ParsedIntentInfo intent : this.intents) {
- intent.packageName = packageName;
- }
- }
-
- public boolean isExported() {
- return exported;
- }
-
- @VisibleForTesting
- public void setExported(boolean exported) {
- this.exported = exported;
- }
-
- public List<ParsedProviderIntentInfo> getIntents() {
- return intents;
- }
-
- public int getFlags() {
- return flags;
- }
-
- public int getOrder() {
- return order;
- }
-
- public void setAuthority(String authority) {
- this.authority = TextUtils.safeIntern(authority);
- }
-
- public String getAuthority() {
- return authority;
- }
-
- public void setSyncable(boolean isSyncable) {
- this.isSyncable = isSyncable;
- }
-
- public boolean isSyncable() {
- return isSyncable;
- }
-
- public void setReadPermission(String readPermission) {
- // Empty string must be converted to null
- this.readPermission = TextUtils.isEmpty(readPermission)
- ? null : readPermission.intern();
- }
-
- public String getReadPermission() {
- return readPermission;
- }
-
- public void setWritePermission(String writePermission) {
- // Empty string must be converted to null
- this.writePermission = TextUtils.isEmpty(writePermission)
- ? null : writePermission.intern();
- }
-
- public String getWritePermission() {
- return writePermission;
- }
-
- public boolean isGrantUriPermissions() {
- return grantUriPermissions;
- }
-
- public boolean isForceUriPermissions() {
- return forceUriPermissions;
- }
-
- public boolean isMultiProcess() {
- return multiProcess;
- }
-
- public int getInitOrder() {
- return initOrder;
- }
-
- public PatternMatcher[] getUriPermissionPatterns() {
- return uriPermissionPatterns;
- }
-
- public PathPermission[] getPathPermissions() {
- return pathPermissions;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeBoolean(this.exported);
- dest.writeInt(this.flags);
- dest.writeInt(this.order);
- dest.writeString(this.authority);
- dest.writeBoolean(this.isSyncable);
- dest.writeString(this.readPermission);
- dest.writeString(this.writePermission);
- dest.writeBoolean(this.grantUriPermissions);
- dest.writeBoolean(this.forceUriPermissions);
- dest.writeBoolean(this.multiProcess);
- dest.writeInt(this.initOrder);
- dest.writeTypedArray(this.uriPermissionPatterns, flags);
- dest.writeTypedArray(this.pathPermissions, flags);
- }
-
- public ParsedProvider() {
- }
-
- protected ParsedProvider(Parcel in) {
- super(in);
- this.exported = in.readByte() != 0;
- this.flags = in.readInt();
- this.order = in.readInt();
- this.authority = TextUtils.safeIntern(in.readString());
- this.isSyncable = in.readByte() != 0;
- this.readPermission = TextUtils.safeIntern(in.readString());
- this.writePermission = TextUtils.safeIntern(in.readString());
- this.grantUriPermissions = in.readByte() != 0;
- this.forceUriPermissions = in.readByte() != 0;
- this.multiProcess = in.readByte() != 0;
- this.initOrder = in.readInt();
- this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
- this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
- }
-
- public static final Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
- @Override
- public ParsedProvider createFromParcel(Parcel source) {
- return new ParsedProvider(source);
- }
-
- @Override
- public ParsedProvider[] newArray(int size) {
- return new ParsedProvider[size];
- }
- };
- }
-
- /**
- * A {@link android.R.styleable#AndroidManifestFeature <feature>} tag parsed from the
- * manifest.
- */
- // @DataClass verifier is broken, hence comment out for now
- public static class ParsedFeature implements Parcelable {
- /** Maximum length of featureId */
- public static final int MAX_FEATURE_ID_LEN = 50;
-
- /** Maximum amount of features per package */
- private static final int MAX_NUM_FEATURES = 1000;
-
- /** Id of the feature */
- public final @NonNull String id;
-
- /** User visible label fo the feature */
- public final @StringRes int label;
-
- /** Ids of previously declared features this feature inherits from */
- public final @NonNull List<String> inheritFrom;
-
- /**
- * @return Is this set of features a valid combination for a single package?
- */
- public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
- if (features == null) {
- return true;
- }
-
- ArraySet<String> featureIds = new ArraySet<>(features.size());
- ArraySet<String> inheritFromFeatureIds = new ArraySet<>();
-
- int numFeatures = features.size();
- if (numFeatures > MAX_NUM_FEATURES) {
- return false;
- }
-
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- boolean wasAdded = featureIds.add(features.get(featureNum).id);
- if (!wasAdded) {
- // feature id is not unique
- return false;
- }
- }
-
- for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
- ParsedFeature feature = features.get(featureNum);
-
- int numInheritFrom = feature.inheritFrom.size();
- for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
- String inheritFrom = feature.inheritFrom.get(inheritFromNum);
-
- if (featureIds.contains(inheritFrom)) {
- // Cannot inherit from a feature that is still defined
- return false;
- }
-
- boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
- if (!wasAdded) {
- // inheritFrom is not unique
- return false;
- }
- }
- }
-
- return true;
- }
-
-
-
- // Code below generated by codegen v1.0.14.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- /**
- * Creates a new ParsedFeature.
- *
- * @param id
- * Id of the feature
- * @param label
- * User visible label fo the feature (if defined as resource)
- * @param inheritFrom
- * Ids of previously declared features this feature inherits from
- */
- @DataClass.Generated.Member
- public ParsedFeature(
- @NonNull String id,
- @StringRes int label,
- @NonNull List<String> inheritFrom) {
- this.id = id;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, id);
- this.label = label;
- com.android.internal.util.AnnotationValidations.validate(
- StringRes.class, null, label);
- this.inheritFrom = inheritFrom;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, inheritFrom);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeString(id);
- dest.writeInt(label);
- dest.writeStringList(inheritFrom);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- protected ParsedFeature(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- String _id = in.readString();
- int _label = in.readInt();
- List<String> _inheritFrom = new ArrayList<>();
- in.readStringList(_inheritFrom);
-
- this.id = _id;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, id);
- this.label = _label;
- com.android.internal.util.AnnotationValidations.validate(
- StringRes.class, null, label);
- this.inheritFrom = _inheritFrom;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, inheritFrom);
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
- = new Parcelable.Creator<ParsedFeature>() {
- @Override
- public ParsedFeature[] newArray(int size) {
- return new ParsedFeature[size];
- }
-
- @Override
- public ParsedFeature createFromParcel(@NonNull Parcel in) {
- return new ParsedFeature(in);
- }
- };
-
- /*@DataClass.Generated(
- time = 1576783172965L,
- codegenVersion = "1.0.14",
- sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java",
- inputSignatures = "public final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
- */
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
- }
-
- public static class ParsedPermission extends ParsedComponent<ParsedIntentInfo> {
-
- public String backgroundPermission;
- private String group;
- public int requestRes;
- public int protectionLevel;
- public boolean tree;
-
- public ParsedPermissionGroup parsedPermissionGroup;
-
- public void setName(String className) {
- this.className = className;
- }
-
- public void setGroup(String group) {
- this.group = TextUtils.safeIntern(group);
- }
-
- public String getGroup() {
- return group;
- }
-
- public boolean isRuntime() {
- return getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
- }
-
- public boolean isAppOp() {
- return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
- }
-
- @PermissionInfo.Protection
- public int getProtection() {
- return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
- }
-
- public int getProtectionFlags() {
- return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
- }
-
- public int calculateFootprint() {
- int size = getName().length();
- if (nonLocalizedLabel != null) {
- size += nonLocalizedLabel.length();
- }
- return size;
- }
-
- public ParsedPermission() {
- }
-
- public ParsedPermission(ParsedPermission other) {
- // TODO(b/135203078): Better way to copy this? Maybe refactor to the point where copy
- // isn't needed.
- this.className = other.className;
- this.icon = other.icon;
- this.labelRes = other.labelRes;
- this.nonLocalizedLabel = other.nonLocalizedLabel;
- this.logo = other.logo;
- this.banner = other.banner;
- this.descriptionRes = other.descriptionRes;
- this.enabled = other.enabled;
- this.directBootAware = other.directBootAware;
- this.flags = other.flags;
- this.setSplitName(other.getSplitName());
- this.setPackageName(other.getPackageName());
-
- this.intents.addAll(other.intents);
-
- if (other.metaData != null) {
- this.metaData = new Bundle();
- this.metaData.putAll(other.metaData);
- }
-
- this.backgroundPermission = other.backgroundPermission;
- this.setGroup(other.group);
- this.requestRes = other.requestRes;
- this.protectionLevel = other.protectionLevel;
- this.tree = other.tree;
-
- this.parsedPermissionGroup = other.parsedPermissionGroup;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(this.backgroundPermission);
- dest.writeString(this.group);
- dest.writeInt(this.requestRes);
- dest.writeInt(this.protectionLevel);
- dest.writeBoolean(this.tree);
- dest.writeParcelable(this.parsedPermissionGroup, flags);
- }
-
- protected ParsedPermission(Parcel in) {
- super(in);
- // We use the boot classloader for all classes that we load.
- final ClassLoader boot = Object.class.getClassLoader();
- this.backgroundPermission = in.readString();
- this.group = TextUtils.safeIntern(in.readString());
- this.requestRes = in.readInt();
- this.protectionLevel = in.readInt();
- this.tree = in.readBoolean();
- this.parsedPermissionGroup = in.readParcelable(boot);
- }
-
- public static final Creator<ParsedPermission> CREATOR = new Creator<ParsedPermission>() {
- @Override
- public ParsedPermission createFromParcel(Parcel source) {
- return new ParsedPermission(source);
- }
-
- @Override
- public ParsedPermission[] newArray(int size) {
- return new ParsedPermission[size];
- }
- };
- }
-
- public static class ParsedPermissionGroup extends ParsedComponent<ParsedIntentInfo> {
-
- public int requestDetailResourceId;
- public int backgroundRequestResourceId;
- public int backgroundRequestDetailResourceId;
-
- public int requestRes;
- public int priority;
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(this.requestDetailResourceId);
- dest.writeInt(this.backgroundRequestResourceId);
- dest.writeInt(this.backgroundRequestDetailResourceId);
- dest.writeInt(this.requestRes);
- dest.writeInt(this.priority);
- }
-
- public ParsedPermissionGroup() {
- }
-
- protected ParsedPermissionGroup(Parcel in) {
- super(in);
- this.requestDetailResourceId = in.readInt();
- this.backgroundRequestResourceId = in.readInt();
- this.backgroundRequestDetailResourceId = in.readInt();
- this.requestRes = in.readInt();
- this.priority = in.readInt();
- }
-
- public static final Creator<ParsedPermissionGroup> CREATOR =
- new Creator<ParsedPermissionGroup>() {
- @Override
- public ParsedPermissionGroup createFromParcel(Parcel source) {
- return new ParsedPermissionGroup(source);
- }
-
- @Override
- public ParsedPermissionGroup[] newArray(int size) {
- return new ParsedPermissionGroup[size];
- }
- };
- }
-
- public static class ParsedInstrumentation extends ParsedComponent<ParsedIntentInfo> {
-
- private String targetPackage;
- private String targetProcesses;
- public boolean handleProfiling;
- public boolean functionalTest;
-
- public ParsedInstrumentation() {
- }
-
- public void setTargetPackage(String targetPackage) {
- this.targetPackage = TextUtils.safeIntern(targetPackage);
- }
-
- public String getTargetPackage() {
- return targetPackage;
- }
-
- public void setTargetProcesses(String targetProcesses) {
- this.targetProcesses = TextUtils.safeIntern(targetProcesses);
- }
-
- public String getTargetProcesses() {
- return targetProcesses;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(this.targetPackage);
- dest.writeString(this.targetProcesses);
- dest.writeBoolean(this.handleProfiling);
- dest.writeBoolean(this.functionalTest);
- }
-
- protected ParsedInstrumentation(Parcel in) {
- super(in);
- this.targetPackage = TextUtils.safeIntern(in.readString());
- this.targetProcesses = TextUtils.safeIntern(in.readString());
- this.handleProfiling = in.readByte() != 0;
- this.functionalTest = in.readByte() != 0;
- }
-
- public static final Creator<ParsedInstrumentation> CREATOR =
- new Creator<ParsedInstrumentation>() {
- @Override
- public ParsedInstrumentation createFromParcel(Parcel source) {
- return new ParsedInstrumentation(source);
- }
-
- @Override
- public ParsedInstrumentation[] newArray(int size) {
- return new ParsedInstrumentation[size];
- }
- };
- }
-
- public static class ParsedProcess implements Parcelable {
-
- public String name;
- @Nullable
- public ArraySet<String> deniedPermissions;
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(this.name);
- final int numDenied = this.deniedPermissions != null
- ? this.deniedPermissions.size() : 0;
- dest.writeInt(numDenied);
- for (int i = 0; i < numDenied; i++) {
- dest.writeString(this.deniedPermissions.valueAt(i));
- }
- }
-
- public ParsedProcess() {
- }
-
- public ParsedProcess(@NonNull ParsedProcess other) {
- name = other.name;
- if (other.deniedPermissions != null) {
- deniedPermissions = new ArraySet<>(other.deniedPermissions);
- }
- }
-
- public void addStateFrom(@NonNull ParsedProcess other) {
- if (other.deniedPermissions != null) {
- for (int i = other.deniedPermissions.size() - 1; i >= 0; i--) {
- if (deniedPermissions == null) {
- deniedPermissions = new ArraySet<>(other.deniedPermissions.size());
- }
- deniedPermissions.add(other.deniedPermissions.valueAt(i));
- }
- }
- }
-
- protected ParsedProcess(Parcel in) {
- this.name = TextUtils.safeIntern(in.readString());
- final int numDenied = in.readInt();
- if (numDenied > 0) {
- this.deniedPermissions = new ArraySet<>(numDenied);
- this.deniedPermissions.add(TextUtils.safeIntern(in.readString()));
- }
- }
-
- public static final Creator<ParsedProcess> CREATOR =
- new Creator<ParsedProcess>() {
- @Override
- public ParsedProcess createFromParcel(Parcel source) {
- return new ParsedProcess(source);
- }
-
- @Override
- public ParsedProcess[] newArray(int size) {
- return new ParsedProcess[size];
- }
- };
- }
-
- public static ParsedActivity parseActivity(
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser, int flags, String[] outError,
- boolean receiver, boolean hardwareAccelerated)
- throws XmlPullParserException, IOException {
-
- TypedArray sa = null;
- boolean visibleToEphemeral;
- boolean setExported;
-
- int targetSdkVersion = parsingPackage.getTargetSdkVersion();
- String packageName = parsingPackage.getPackageName();
- String packageProcessName = parsingPackage.getProcessName();
- ParsedActivity result = new ParsedActivity();
-
- try {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
-
- String tag = receiver ? "<receiver>" : "<activity>";
-
- String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_name, 0);
- if (name == null) {
- outError[0] = tag + " does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = tag + " invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestActivity_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivity_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivity_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivity_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivity_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- CharSequence pname;
- if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
- pname = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_process,
- Configuration.NATIVE_CONFIG_VERSION);
- } else {
- // Some older apps have been seen to use a resource reference
- // here that on older builds was ignored (with a warning). We
- // need to continue to do this for them so they don't break.
- pname = sa.getNonResourceString(R.styleable.AndroidManifestActivity_process);
- }
-
- result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
- packageProcessName, pname,
- flags, separateProcesses, outError));
-
- result.descriptionRes = sa.getResourceId(
- R.styleable.AndroidManifestActivity_description, 0);
-
- result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivity_enabled, true);
-
- setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
- if (setExported) {
- result.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported,
- false);
- }
-
- result.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
-
- result.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
- parsingPackage.getUiOptions());
-
- String parentName = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivity_parentActivityName,
- Configuration.NATIVE_CONFIG_VERSION);
- if (parentName != null) {
- String parentClassName = ApkParseUtils.buildClassName(packageName, parentName);
- if (parentClassName == null) {
- Log.e(TAG,
- "Activity " + result.className
- + " specified invalid parentActivityName " +
- parentName);
- } else {
- result.parentActivityName = parentClassName;
- }
- }
-
- String str;
- str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
- if (str == null) {
- result.setPermission(parsingPackage.getPermission());
- } else {
- result.setPermission(str);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivity_taskAffinity,
- Configuration.NATIVE_CONFIG_VERSION);
- result.taskAffinity = PackageParser.buildTaskAffinityName(
- packageName,
- parsingPackage.getTaskAffinity(), str, outError);
-
- result.setSplitName(
- sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0));
-
- result.flags = 0;
- if (sa.getBoolean(
- R.styleable.AndroidManifestActivity_multiprocess, false)) {
- result.flags |= ActivityInfo.FLAG_MULTIPROCESS;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
- result.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
- result.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
- result.flags |= ActivityInfo.FLAG_NO_HISTORY;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
- result.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
- result.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
- result.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
- (parsingPackage.getFlags() & ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
- != 0)) {
- result.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs,
- false)) {
- result.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
- || sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
- result.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
- result.flags |= ActivityInfo.FLAG_IMMERSIVE;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
- result.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
- }
-
- boolean directBootAware;
-
- if (!receiver) {
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
- hardwareAccelerated)) {
- result.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
- }
-
- result.launchMode = sa.getInt(
- R.styleable.AndroidManifestActivity_launchMode,
- ActivityInfo.LAUNCH_MULTIPLE);
- result.documentLaunchMode = sa.getInt(
- R.styleable.AndroidManifestActivity_documentLaunchMode,
- ActivityInfo.DOCUMENT_LAUNCH_NONE);
- result.maxRecents = sa.getInt(
- R.styleable.AndroidManifestActivity_maxRecents,
- ActivityTaskManager.getDefaultAppRecentsLimitStatic());
- result.configChanges = PackageParser.getActivityConfigChanges(
- sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
- sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
- result.softInputMode = sa.getInt(
- R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
-
- result.persistableMode = sa.getInteger(
- R.styleable.AndroidManifestActivity_persistableMode,
- ActivityInfo.PERSIST_ROOT_ONLY);
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
- result.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents,
- false)) {
- result.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
- false)) {
- result.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
- result.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
- }
-
- int screenOrientation = sa.getInt(
- R.styleable.AndroidManifestActivity_screenOrientation,
- SCREEN_ORIENTATION_UNSPECIFIED);
- result.screenOrientation = screenOrientation;
-
- int resizeMode = getActivityResizeMode(parsingPackage, sa, screenOrientation);
- result.resizeMode = resizeMode;
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
- false)) {
- result.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
- result.flags |= FLAG_ALWAYS_FOCUSABLE;
- }
-
- if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
- && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
- == TypedValue.TYPE_FLOAT) {
- result.setMaxAspectRatio(resizeMode,
- sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
- 0 /*default*/));
- }
-
- if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
- && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
- == TypedValue.TYPE_FLOAT) {
- result.setMinAspectRatio(resizeMode,
- sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
- 0 /*default*/));
- }
-
- result.lockTaskLaunchMode =
- sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
-
- directBootAware = sa.getBoolean(
- R.styleable.AndroidManifestActivity_directBootAware,
- false);
-
- result.requestedVrComponent =
- sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
-
- result.rotationAnimation =
- sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation,
- ROTATION_ANIMATION_UNSPECIFIED);
-
- result.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
- ActivityInfo.COLOR_MODE_DEFAULT);
-
- result.preferMinimalPostProcessing = sa.getBoolean(
- R.styleable.AndroidManifestActivity_preferMinimalPostProcessing,
- ActivityInfo.MINIMAL_POST_PROCESSING_DEFAULT);
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_showWhenLocked, false)) {
- result.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_turnScreenOn, false)) {
- result.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
- }
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked,
- false)) {
- result.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
- }
- } else {
- result.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- result.configChanges = 0;
-
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
- result.flags |= ActivityInfo.FLAG_SINGLE_USER;
- }
- directBootAware = sa.getBoolean(
- R.styleable.AndroidManifestActivity_directBootAware,
- false);
- }
-
- result.directBootAware = directBootAware;
-
- if (directBootAware) {
- parsingPackage.setPartiallyDirectBootAware(true);
- }
-
- // can't make this final; we may set it later via meta-data
- visibleToEphemeral = sa.getBoolean(
- R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
- if (visibleToEphemeral) {
- result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- parsingPackage.setVisibleToInstantApps(true);
- }
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
-
- if (receiver && (parsingPackage.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
- // A heavy-weight application can not have receives in its main process
- if (result.getProcessName().equals(packageName)) {
- outError[0] = "Heavy-weight applications can not have receivers in main process";
- return null;
- }
- }
-
- if (outError[0] != null) {
- return null;
- }
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (parser.getName().equals("intent-filter")) {
- ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName,
- result.className);
- if (!parseIntentInfo(intentInfo, parsingPackage, res, parser,
- true /*allowGlobs*/,
- true /*allowAutoVerify*/, outError)) {
- return null;
- }
- if (intentInfo.countActions() == 0) {
- Slog.w(TAG, "No actions in intent filter at "
- + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- } else {
- result.order = Math.max(intentInfo.getOrder(), result.order);
- result.addIntent(intentInfo);
- }
- // adjust activity flags when we implicitly expose it via a browsable filter
- final int visibility = visibleToEphemeral
- ? IntentFilter.VISIBILITY_EXPLICIT
- : !receiver && isImplicitlyExposedIntent(intentInfo)
- ? IntentFilter.VISIBILITY_IMPLICIT
- : IntentFilter.VISIBILITY_NONE;
- intentInfo.setVisibilityToInstantApp(visibility);
- if (intentInfo.isVisibleToInstantApp()) {
- result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- }
- if (intentInfo.isImplicitlyVisibleToInstantApp()) {
- result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
- }
- if (PackageParser.LOG_UNSAFE_BROADCASTS && receiver
- && (targetSdkVersion >= Build.VERSION_CODES.O)) {
- for (int i = 0; i < intentInfo.countActions(); i++) {
- final String action = intentInfo.getAction(i);
- if (action == null || !action.startsWith("android.")) continue;
- if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
- Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
- + packageName + " as requested at: "
- + parser.getPositionDescription());
- }
- }
- }
- } else if (!receiver && parser.getName().equals("preferred")) {
- ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName,
- result.className);
- if (!parseIntentInfo(intentInfo, parsingPackage, res, parser,
- false /*allowGlobs*/,
- false /*allowAutoVerify*/, outError)) {
- return null;
- }
- // adjust activity flags when we implicitly expose it via a browsable filter
- final int visibility = visibleToEphemeral
- ? IntentFilter.VISIBILITY_EXPLICIT
- : !receiver && isImplicitlyExposedIntent(intentInfo)
- ? IntentFilter.VISIBILITY_IMPLICIT
- : IntentFilter.VISIBILITY_NONE;
- intentInfo.setVisibilityToInstantApp(visibility);
- if (intentInfo.isVisibleToInstantApp()) {
- result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- }
- if (intentInfo.isImplicitlyVisibleToInstantApp()) {
- result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
- }
-
- if (intentInfo.countActions() == 0) {
- Slog.w(TAG, "No actions in preferred at "
- + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- } else {
- parsingPackage.addPreferredActivityFilter(intentInfo);
- }
- } else if (parser.getName().equals("meta-data")) {
- if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
- result.metaData,
- outError)) == null) {
- return null;
- }
- } else if (!receiver && parser.getName().equals("layout")) {
- result.windowLayout = parseLayout(res, parser);
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Problem in package " + parsingPackage.getBaseCodePath() + ":");
- if (receiver) {
- Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- } else {
- Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- }
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- if (receiver) {
- outError[0] = "Bad element under <receiver>: " + parser.getName();
- } else {
- outError[0] = "Bad element under <activity>: " + parser.getName();
- }
- return null;
- }
- }
- }
-
- if (!setExported) {
- result.exported = result.intents.size() > 0;
- }
-
- return result;
- }
-
- public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
- return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
- || intentInfo.hasAction(Intent.ACTION_SEND)
- || intentInfo.hasAction(Intent.ACTION_SENDTO)
- || intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE);
- }
-
- public static int getActivityResizeMode(
- ParsingPackage parsingPackage,
- TypedArray sa,
- int screenOrientation
- ) {
- int privateFlags = parsingPackage.getPrivateFlags();
- final boolean appExplicitDefault = (privateFlags
- & (ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
- | ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0;
-
- if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
- || appExplicitDefault) {
- // Activity or app explicitly set if it is resizeable or not;
- final boolean appResizeable = (privateFlags
- & ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0;
- if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
- appResizeable)) {
- return ActivityInfo.RESIZE_MODE_RESIZEABLE;
- } else {
- return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- }
- }
-
- if ((privateFlags
- & ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
- != 0) {
- // The activity or app didn't explicitly set the resizing option, however we want to
- // make it resize due to the sdk version it is targeting.
- return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
- }
-
- // resize preference isn't set and target sdk version doesn't support resizing apps by
- // default. For the app to be resizeable if it isn't fixed orientation or immersive.
- if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
- return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
- } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
- return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
- } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
- return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
- } else {
- return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
- }
- }
-
- public static ParsedService parseService(
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser, int flags, String[] outError
- ) throws XmlPullParserException, IOException {
- TypedArray sa = null;
- boolean visibleToEphemeral;
- boolean setExported;
-
- String packageName = parsingPackage.getPackageName();
- String packageProcessName = parsingPackage.getProcessName();
- ParsedService result = new ParsedService();
-
- try {
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestService);
-
- String name = sa.getNonConfigurationString(R.styleable.AndroidManifestService_name, 0);
- if (name == null) {
- outError[0] = "<service> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<service> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestService_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestService_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestService_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestService_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestService_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- CharSequence pname;
- if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
- pname = sa.getNonConfigurationString(R.styleable.AndroidManifestService_process,
- Configuration.NATIVE_CONFIG_VERSION);
- } else {
- // Some older apps have been seen to use a resource reference
- // here that on older builds was ignored (with a warning). We
- // need to continue to do this for them so they don't break.
- pname = sa.getNonResourceString(R.styleable.AndroidManifestService_process);
- }
-
- result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
- packageProcessName, pname,
- flags, separateProcesses, outError));
-
- result.descriptionRes = sa.getResourceId(
- R.styleable.AndroidManifestService_description, 0);
-
- result.enabled = sa.getBoolean(R.styleable.AndroidManifestService_enabled, true);
-
- setExported = sa.hasValue(
- R.styleable.AndroidManifestService_exported);
- if (setExported) {
- result.exported = sa.getBoolean(
- R.styleable.AndroidManifestService_exported, false);
- }
-
- String str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestService_permission, 0);
- if (str == null) {
- result.setPermission(parsingPackage.getPermission());
- } else {
- result.setPermission(str);
- }
-
- result.setSplitName(
- sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0));
-
- result.foregroundServiceType = sa.getInt(
- R.styleable.AndroidManifestService_foregroundServiceType,
- ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
-
- result.flags = 0;
- if (sa.getBoolean(
- R.styleable.AndroidManifestService_stopWithTask,
- false)) {
- result.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
- }
- if (sa.getBoolean(
- R.styleable.AndroidManifestService_isolatedProcess,
- false)) {
- result.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
- }
- if (sa.getBoolean(
- R.styleable.AndroidManifestService_externalService,
- false)) {
- result.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
- }
- if (sa.getBoolean(
- R.styleable.AndroidManifestService_useAppZygote,
- false)) {
- result.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
- }
- if (sa.getBoolean(
- R.styleable.AndroidManifestService_singleUser,
- false)) {
- result.flags |= ServiceInfo.FLAG_SINGLE_USER;
- }
-
- result.directBootAware = sa.getBoolean(
- R.styleable.AndroidManifestService_directBootAware,
- false);
- if (result.directBootAware) {
- parsingPackage.setPartiallyDirectBootAware(true);
- }
-
- visibleToEphemeral = sa.getBoolean(
- R.styleable.AndroidManifestService_visibleToInstantApps, false);
- if (visibleToEphemeral) {
- result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- parsingPackage.setVisibleToInstantApps(true);
- }
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- if (parsingPackage.cantSaveState()) {
- // A heavy-weight application can not have services in its main process
- // We can do direct compare because we intern all strings.
- if (Objects.equals(result.getProcessName(), parsingPackage.getPackageName())) {
- outError[0] = "Heavy-weight applications can not have services in main process";
- return null;
- }
- }
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (parser.getName().equals("intent-filter")) {
- ParsedServiceIntentInfo intent = new ParsedServiceIntentInfo(packageName,
- result.className);
- if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
- false /*allowAutoVerify*/,
- outError)) {
- return null;
- }
- if (visibleToEphemeral) {
- intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- }
- result.order = Math.max(intent.getOrder(), result.order);
- result.intents.add(intent);
- } else if (parser.getName().equals("meta-data")) {
- if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
- result.metaData,
- outError)) == null) {
- return null;
- }
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <service>: "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- outError[0] = "Bad element under <service>: " + parser.getName();
- return null;
- }
- }
- }
-
- if (!setExported) {
- result.exported = result.intents.size() > 0;
- }
-
- return result;
- }
-
- public static ParsedProvider parseProvider(
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser, int flags, String[] outError)
- throws XmlPullParserException, IOException {
- TypedArray sa = null;
- String cpname;
- boolean visibleToEphemeral;
-
- int targetSdkVersion = parsingPackage.getTargetSdkVersion();
- String packageName = parsingPackage.getPackageName();
- String packageProcessName = parsingPackage.getProcessName();
- ParsedProvider result = new ParsedProvider();
-
- try {
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestProvider);
-
- String name = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_name, 0);
- if (name == null) {
- outError[0] = "<provider> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<provider> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestProvider_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestProvider_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestProvider_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestProvider_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestProvider_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- CharSequence pname;
- if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
- pname = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_process,
- Configuration.NATIVE_CONFIG_VERSION);
- } else {
- // Some older apps have been seen to use a resource reference
- // here that on older builds was ignored (with a warning). We
- // need to continue to do this for them so they don't break.
- pname = sa.getNonResourceString(R.styleable.AndroidManifestProvider_process);
- }
-
- result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
- packageProcessName, pname,
- flags, separateProcesses, outError));
-
- result.descriptionRes = sa.getResourceId(
- R.styleable.AndroidManifestProvider_description, 0);
-
- result.enabled = sa.getBoolean(R.styleable.AndroidManifestProvider_enabled, true);
-
- boolean providerExportedDefault = false;
-
- if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
- // For compatibility, applications targeting API level 16 or lower
- // should have their content providers exported by default, unless they
- // specify otherwise.
- providerExportedDefault = true;
- }
-
- result.exported = sa.getBoolean(
- R.styleable.AndroidManifestProvider_exported,
- providerExportedDefault);
-
- cpname = sa.getNonConfigurationString(
- R.styleable.AndroidManifestProvider_authorities, 0);
-
- result.isSyncable = sa.getBoolean(
- R.styleable.AndroidManifestProvider_syncable,
- false);
-
- String permission = sa.getNonConfigurationString(
- R.styleable.AndroidManifestProvider_permission, 0);
- String str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestProvider_readPermission, 0);
- if (str == null) {
- str = permission;
- }
- if (str == null) {
- result.setReadPermission(parsingPackage.getPermission());
- } else {
- result.setReadPermission(str);
- }
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestProvider_writePermission, 0);
- if (str == null) {
- str = permission;
- }
- if (str == null) {
- result.setWritePermission(parsingPackage.getPermission());
- } else {
- result.setWritePermission(str);
- }
-
- result.grantUriPermissions = sa.getBoolean(
- R.styleable.AndroidManifestProvider_grantUriPermissions,
- false);
-
- result.forceUriPermissions = sa.getBoolean(
- R.styleable.AndroidManifestProvider_forceUriPermissions,
- false);
-
- result.multiProcess = sa.getBoolean(
- R.styleable.AndroidManifestProvider_multiprocess,
- false);
-
- result.initOrder = sa.getInt(
- R.styleable.AndroidManifestProvider_initOrder,
- 0);
-
- result.setSplitName(
- sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0));
-
- result.flags = 0;
-
- if (sa.getBoolean(
- R.styleable.AndroidManifestProvider_singleUser,
- false)) {
- result.flags |= ProviderInfo.FLAG_SINGLE_USER;
- }
-
- result.directBootAware = sa.getBoolean(
- R.styleable.AndroidManifestProvider_directBootAware,
- false);
- if (result.directBootAware) {
- parsingPackage.setPartiallyDirectBootAware(true);
- }
-
- visibleToEphemeral =
- sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
- if (visibleToEphemeral) {
- result.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- parsingPackage.setVisibleToInstantApps(true);
- }
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- if ((parsingPackage.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
- != 0) {
- // A heavy-weight application can not have providers in its main process
- if (result.getProcessName().equals(packageName)) {
- outError[0] = "Heavy-weight applications can not have providers in main process";
- return null;
- }
- }
-
- if (cpname == null) {
- outError[0] = "<provider> does not include authorities attribute";
- return null;
- }
- if (cpname.length() <= 0) {
- outError[0] = "<provider> has empty authorities attribute";
- return null;
- }
- result.setAuthority(cpname);
-
- if (!parseProviderTags(parsingPackage, res, parser, visibleToEphemeral, result, outError)) {
- return null;
- }
-
- return result;
- }
-
- public static ParsedQueriesIntentInfo parsedParsedQueriesIntentInfo(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- ParsedQueriesIntentInfo intentInfo = new ParsedQueriesIntentInfo(
- parsingPackage.getPackageName(),
- null
- );
- if (!parseIntentInfo(
- intentInfo,
- parsingPackage,
- res,
- parser,
- true /*allowGlobs*/,
- true /*allowAutoVerify*/,
- outError
- )) {
- return null;
- }
- return intentInfo;
- }
-
- private static boolean parseProviderTags(
- ParsingPackage parsingPackage,
- Resources res, XmlResourceParser parser,
- boolean visibleToEphemeral, ParsedProvider outInfo, String[] outError)
- throws XmlPullParserException, IOException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (parser.getName().equals("intent-filter")) {
- ParsedProviderIntentInfo intent = new ParsedProviderIntentInfo(
- parsingPackage.getPackageName(), outInfo.className);
- if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
- false /*allowAutoVerify*/,
- outError)) {
- return false;
- }
- if (visibleToEphemeral) {
- intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
- outInfo.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")) {
- Bundle metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
- outInfo.metaData, outError);
- if (metaData == null) {
- return false;
- } else {
- outInfo.metaData = metaData;
- }
-
- } else if (parser.getName().equals("grant-uri-permission")) {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestGrantUriPermission);
-
- PatternMatcher pa = null;
-
- String str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestGrantUriPermission_path, 0);
- if (str != null) {
- pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
- if (str != null) {
- pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
- if (str != null) {
- pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
- }
-
- sa.recycle();
-
- if (pa != null) {
- if (outInfo.uriPermissionPatterns == null) {
- outInfo.uriPermissionPatterns = new PatternMatcher[1];
- outInfo.uriPermissionPatterns[0] = pa;
- } else {
- final int N = outInfo.uriPermissionPatterns.length;
- PatternMatcher[] newp = new PatternMatcher[N + 1];
- System.arraycopy(outInfo.uriPermissionPatterns, 0, newp, 0, N);
- newp[N] = pa;
- outInfo.uriPermissionPatterns = newp;
- }
- outInfo.grantUriPermissions = true;
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <path-permission>: "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath()
- + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
- return false;
- }
- }
- XmlUtils.skipCurrentTag(parser);
-
- } else if (parser.getName().equals("path-permission")) {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestPathPermission);
-
- PathPermission pa = null;
-
- String permission = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_permission, 0);
- String readPermission = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_readPermission, 0);
- if (readPermission == null) {
- readPermission = permission;
- }
- String writePermission = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_writePermission, 0);
- if (writePermission == null) {
- writePermission = permission;
- }
-
- boolean havePerm = false;
- if (readPermission != null) {
- readPermission = readPermission.intern();
- havePerm = true;
- }
- if (writePermission != null) {
- writePermission = writePermission.intern();
- havePerm = true;
- }
-
- if (!havePerm) {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "No readPermission or writePermssion for <path-permission>: "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath()
- + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- outError[0] = "No readPermission or writePermssion for <path-permission>";
- return false;
- }
- }
-
- String path = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_path, 0);
- if (path != null) {
- pa = new PathPermission(path,
- PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
- }
-
- path = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
- if (path != null) {
- pa = new PathPermission(path,
- PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
- }
-
- path = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_pathPattern, 0);
- if (path != null) {
- pa = new PathPermission(path,
- PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
- }
-
- path = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
- if (path != null) {
- pa = new PathPermission(path,
- PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission);
- }
-
- sa.recycle();
-
- if (pa != null) {
- if (outInfo.pathPermissions == null) {
- outInfo.pathPermissions = new PathPermission[1];
- outInfo.pathPermissions[0] = pa;
- } else {
- final int N = outInfo.pathPermissions.length;
- PathPermission[] newp = new PathPermission[N + 1];
- System.arraycopy(outInfo.pathPermissions, 0, newp, 0, N);
- newp[N] = pa;
- outInfo.pathPermissions = newp;
- }
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath()
- + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
- return false;
- }
- XmlUtils.skipCurrentTag(parser);
-
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <provider>: "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- outError[0] = "Bad element under <provider>: " + parser.getName();
- return false;
- }
- }
- }
- return true;
- }
-
- public static ParsedActivity parseActivityAlias(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- String[] outError)
- throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestActivityAlias);
-
- String targetActivity = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivityAlias_targetActivity,
- Configuration.NATIVE_CONFIG_VERSION);
- if (targetActivity == null) {
- outError[0] = "<activity-alias> does not specify android:targetActivity";
- sa.recycle();
- return null;
- }
-
- String packageName = parsingPackage.getPackageName();
- targetActivity = ApkParseUtils.buildClassName(packageName, targetActivity);
- if (targetActivity == null) {
- outError[0] = "Empty class name in package " + packageName;
- sa.recycle();
- return null;
- }
-
- ParsedActivity target = null;
-
- List<ParsedActivity> activities = parsingPackage.getActivities();
- final int NA = activities.size();
- for (int i = 0; i < NA; i++) {
- ParsedActivity t = activities.get(i);
- if (targetActivity.equals(t.className)) {
- target = t;
- break;
- }
- }
-
- if (target == null) {
- outError[0] = "<activity-alias> target activity " + targetActivity
- + " not found in manifest with activities = " + parsingPackage.getActivities()
- + ", parsedActivities = " + activities;
- sa.recycle();
- return null;
- }
-
- ParsedActivity result = new ParsedActivity();
- result.setPackageNameInternal(target.getPackageName());
- result.targetActivity = targetActivity;
- result.configChanges = target.configChanges;
- result.flags = target.flags;
- result.privateFlags = target.privateFlags;
- result.icon = target.icon;
- result.logo = target.logo;
- result.banner = target.banner;
- result.labelRes = target.labelRes;
- result.nonLocalizedLabel = target.nonLocalizedLabel;
- result.launchMode = target.launchMode;
- result.lockTaskLaunchMode = target.lockTaskLaunchMode;
- result.descriptionRes = target.descriptionRes;
- result.screenOrientation = target.screenOrientation;
- result.taskAffinity = target.taskAffinity;
- result.theme = target.theme;
- result.softInputMode = target.softInputMode;
- result.uiOptions = target.uiOptions;
- result.parentActivityName = target.parentActivityName;
- result.maxRecents = target.maxRecents;
- result.windowLayout = target.windowLayout;
- result.resizeMode = target.resizeMode;
- result.maxAspectRatio = target.maxAspectRatio;
- result.hasMaxAspectRatio = target.hasMaxAspectRatio;
- result.minAspectRatio = target.minAspectRatio;
- result.hasMinAspectRatio = target.hasMinAspectRatio;
- result.requestedVrComponent = target.requestedVrComponent;
- result.directBootAware = target.directBootAware;
-
- result.setProcessName(parsingPackage.getAppInfoProcessName(), target.getProcessName());
-
- // Not all attributes from the target ParsedActivity are copied to the alias.
- // Careful when adding an attribute and determine whether or not it should be copied.
-// result.enabled = target.enabled;
-// result.exported = target.exported;
-// result.permission = target.permission;
-// result.splitName = target.splitName;
-// result.documentLaunchMode = target.documentLaunchMode;
-// result.persistableMode = target.persistableMode;
-// result.rotationAnimation = target.rotationAnimation;
-// result.colorMode = target.colorMode;
-// result.intents.addAll(target.intents);
-// result.order = target.order;
-// result.metaData = target.metaData;
-
- String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivityAlias_name,
- 0);
- if (name == null) {
- outError[0] = "<activity-alias> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<activity-alias> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestActivityAlias_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivityAlias_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- result.descriptionRes = sa.getResourceId(
- R.styleable.AndroidManifestActivityAlias_description, 0);
-
- result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivityAlias_enabled, true);
-
- final boolean setExported = sa.hasValue(
- R.styleable.AndroidManifestActivityAlias_exported);
- if (setExported) {
- result.exported = sa.getBoolean(
- R.styleable.AndroidManifestActivityAlias_exported, false);
- }
-
- String str;
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivityAlias_permission, 0);
- if (str != null) {
- result.setPermission(str);
- }
-
- String parentName = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivityAlias_parentActivityName,
- Configuration.NATIVE_CONFIG_VERSION);
- if (parentName != null) {
- String parentClassName = ApkParseUtils.buildClassName(result.getPackageName(),
- parentName);
- if (parentClassName == null) {
- Log.e(TAG, "Activity alias " + result.className +
- " specified invalid parentActivityName " + parentName);
- outError[0] = null;
- } else {
- result.parentActivityName = parentClassName;
- }
- }
-
- // TODO add visibleToInstantApps attribute to activity alias
- final boolean visibleToEphemeral =
- ((result.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
-
- sa.recycle();
-
- if (outError[0] != null) {
- return null;
- }
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("intent-filter")) {
- ParsedActivityIntentInfo intent = new ParsedActivityIntentInfo(packageName,
- result.className);
- if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
- true /*allowAutoVerify*/, outError)) {
- return null;
- }
- if (intent.countActions() == 0) {
- Slog.w(TAG, "No actions in intent filter at "
- + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- } else {
- result.order = Math.max(intent.getOrder(), result.order);
- result.addIntent(intent);
- }
- // adjust activity flags when we implicitly expose it via a browsable filter
- final int visibility = visibleToEphemeral
- ? IntentFilter.VISIBILITY_EXPLICIT
- : isImplicitlyExposedIntent(intent)
- ? IntentFilter.VISIBILITY_IMPLICIT
- : IntentFilter.VISIBILITY_NONE;
- intent.setVisibilityToInstantApp(visibility);
- if (intent.isVisibleToInstantApp()) {
- result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
- }
- if (intent.isImplicitlyVisibleToInstantApp()) {
- result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
- }
- } else if (tagName.equals("meta-data")) {
- if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
- result.metaData,
- outError)) == null) {
- return null;
- }
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <activity-alias>: " + tagName
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- outError[0] = "Bad element under <activity-alias>: " + tagName;
- return null;
- }
- }
- }
-
- if (!setExported) {
- result.exported = result.intents.size() > 0;
- }
-
- return result;
- }
-
- public static ParsedFeature parseFeature(
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- String featureId;
- int label;
- List<String> inheritFrom = null;
-
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
- if (sa == null) {
- outError[0] = "<feature> could not be parsed";
- return null;
- }
-
- try {
- featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
- 0);
- if (featureId == null) {
- outError[0] = "<featureId> does not specify android:featureId";
- return null;
- }
- if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) {
- outError[0] = "<featureId> is too long. Max length is "
- + ParsedFeature.MAX_FEATURE_ID_LEN;
- return null;
- }
-
- label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
- if (label == Resources.ID_NULL) {
- outError[0] = "<featureId> does not specify android:label";
- return null;
- }
- } finally {
- sa.recycle();
- }
-
- int type;
- final int innerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("inherit-from")) {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
- if (sa == null) {
- outError[0] = "<inherit-from> could not be parsed";
- return null;
- }
-
- try {
- String inheritFromId = sa.getNonConfigurationString(
- R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);
-
- if (inheritFrom == null) {
- inheritFrom = new ArrayList<>();
- }
- inheritFrom.add(inheritFromId);
- } finally {
- sa.recycle();
- }
- } else {
- outError[0] = "Bad element under <feature>: " + tagName;
- return null;
- }
- }
-
- if (inheritFrom == null) {
- inheritFrom = Collections.emptyList();
- } else {
- ((ArrayList) inheritFrom).trimToSize();
- }
-
- return new ParsedFeature(featureId, label, inheritFrom);
- }
-
- public static ParsedPermission parsePermission(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = null;
- String packageName = parsingPackage.getPackageName();
- ParsedPermission result = new ParsedPermission();
-
- try {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
-
- String name = sa.getNonConfigurationString(R.styleable.AndroidManifestPermission_name,
- 0);
- if (name == null) {
- outError[0] = "<permission> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<permission> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestPermission_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermission_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermission_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermission_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermission_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- result.descriptionRes = sa.getResourceId(
- R.styleable.AndroidManifestPermission_description, 0);
-
- if (sa.hasValue(
- R.styleable.AndroidManifestPermission_backgroundPermission)) {
- if ("android".equals(packageName)) {
- result.backgroundPermission = sa.getNonResourceString(
- R.styleable
- .AndroidManifestPermission_backgroundPermission);
- } else {
- Slog.w(TAG, packageName + " defines a background permission. Only the "
- + "'android' package can do that.");
- }
- }
-
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- result.setGroup(sa.getNonResourceString(
- R.styleable.AndroidManifestPermission_permissionGroup));
-
- result.requestRes = sa.getResourceId(
- R.styleable.AndroidManifestPermission_request, 0);
-
- result.protectionLevel = sa.getInt(
- R.styleable.AndroidManifestPermission_protectionLevel,
- PermissionInfo.PROTECTION_NORMAL);
-
- result.flags = sa.getInt(
- R.styleable.AndroidManifestPermission_permissionFlags, 0);
-
- // For now only platform runtime permissions can be restricted
- if (!result.isRuntime() || !"android".equals(result.getPackageName())) {
- result.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
- result.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
- } else {
- // The platform does not get to specify conflicting permissions
- if ((result.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
- && (result.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
- throw new IllegalStateException("Permission cannot be both soft and hard"
- + " restricted: " + result.getName());
- }
- }
-
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- if (result.protectionLevel == -1) {
- outError[0] = "<permission> does not specify protectionLevel";
- return null;
- }
-
- result.protectionLevel = PermissionInfo.fixProtectionLevel(result.protectionLevel);
-
- if (result.getProtectionFlags() != 0) {
- if ((result.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
- && (result.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
- == 0
- && (result.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) !=
- PermissionInfo.PROTECTION_SIGNATURE) {
- outError[0] = "<permission> protectionLevel specifies a non-instant flag but is "
- + "not based on signature type";
- return null;
- }
- }
-
- boolean success = parseAllMetaData(parsingPackage, res, parser,
- "<permission>", result, outError);
- if (!success || outError[0] != null) {
- return null;
- }
-
- return result;
- }
-
- public static ParsedPermission parsePermissionTree(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = null;
- String packageName = parsingPackage.getPackageName();
- ParsedPermission result = new ParsedPermission();
-
- try {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
-
- String name = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPermissionTree_name, 0);
- if (name == null) {
- outError[0] = "<permission-tree> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<permission-tree> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestPermissionTree_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionTree_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- int index = result.getName().indexOf('.');
- if (index > 0) {
- index = result.getName().indexOf('.', index + 1);
- }
- if (index < 0) {
- outError[0] =
- "<permission-tree> name has less than three segments: " + result.getName();
- return null;
- }
-
- result.descriptionRes = 0;
- result.requestRes = 0;
- result.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
- result.tree = true;
-
- boolean success = parseAllMetaData(parsingPackage, res, parser,
- "<permission-tree>", result, outError);
- if (!success || outError[0] != null) {
- return null;
- }
-
- return result;
- }
-
- public static ParsedPermissionGroup parsePermissionGroup(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = null;
- String packageName = parsingPackage.getPackageName();
- ParsedPermissionGroup result = new ParsedPermissionGroup();
-
- try {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
-
- String name = sa.getNonConfigurationString(
- R.styleable.AndroidManifestPermissionGroup_name, 0);
- if (name == null) {
- outError[0] = "<permission> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<permission> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestPermissionGroup_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionGroup_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- result.descriptionRes = sa.getResourceId(
- R.styleable.AndroidManifestPermissionGroup_description, 0);
-
- result.requestDetailResourceId = sa.getResourceId(
- R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
- result.backgroundRequestResourceId = sa.getResourceId(
- R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
- 0);
- result.backgroundRequestDetailResourceId = sa.getResourceId(
- R.styleable
- .AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
-
- result.requestRes = sa.getResourceId(
- R.styleable.AndroidManifestPermissionGroup_request, 0);
- result.flags = sa.getInt(
- R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,
- 0);
- result.priority = sa.getInt(
- R.styleable.AndroidManifestPermissionGroup_priority, 0);
-
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- boolean success = parseAllMetaData(parsingPackage, res, parser,
- "<permission-group>", result, outError);
- if (!success || outError[0] != null) {
- return null;
- }
-
- return result;
- }
-
- public static ParsedInstrumentation parseInstrumentation(
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = null;
- String packageName = parsingPackage.getPackageName();
- ParsedInstrumentation result = new ParsedInstrumentation();
-
- try {
- sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
-
- // TODO(b/135203078): Re-share all of the configuration for this. ParseComponentArgs was
- // un-used for this, but can be adjusted and re-added to share all the initial result
- // parsing for icon/logo/name/etc in all of these parse methods.
- String name = sa.getNonConfigurationString(
- R.styleable.AndroidManifestInstrumentation_name, 0);
- if (name == null) {
- outError[0] = "<instrumentation> does not specify android:name";
- return null;
- } else {
- String className = ApkParseUtils.buildClassName(packageName, name);
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
- outError[0] = "<instrumentation> invalid android:name";
- return null;
- } else if (className == null) {
- outError[0] = "Empty class name in package " + packageName;
- return null;
- }
-
- result.className = className;
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestInstrumentation_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- result.icon = roundIconVal;
- result.nonLocalizedLabel = null;
- } else {
- int iconVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_icon, 0);
- if (iconVal != 0) {
- result.icon = iconVal;
- result.nonLocalizedLabel = null;
- }
- }
-
- int logoVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_logo, 0);
- if (logoVal != 0) {
- result.logo = logoVal;
- }
-
- int bannerVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_banner, 0);
- if (bannerVal != 0) {
- result.banner = bannerVal;
- }
-
- TypedValue v = sa.peekValue(R.styleable.AndroidManifestInstrumentation_label);
- if (v != null && (result.labelRes = v.resourceId) == 0) {
- result.nonLocalizedLabel = v.coerceToString();
- }
-
- result.setPackageNameInternal(packageName);
-
- String str;
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- str = sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage);
- result.setTargetPackage(str);
-
- str = sa.getNonResourceString(
- R.styleable.AndroidManifestInstrumentation_targetProcesses);
- result.setTargetProcesses(str);
- result.handleProfiling = sa.getBoolean(
- R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
- result.functionalTest = sa.getBoolean(
- R.styleable.AndroidManifestInstrumentation_functionalTest, false);
-
- } finally {
- if (sa != null) {
- sa.recycle();
- }
- }
-
- boolean success = parseAllMetaData(parsingPackage, res, parser,
- "<instrumentation>", result, outError);
- if (!success || outError[0] != null) {
- return null;
- }
-
- return result;
- }
-
- private static @Nullable ArraySet<String> parseDenyPermission(
- ArraySet<String> perms,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
- if (sa == null) {
- outError[0] = "<deny-permission> could not be parsed";
- return null;
- }
-
- try {
- String perm = sa.getNonConfigurationString(
- R.styleable.AndroidManifestDenyPermission_name,0);
- if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
- if (perms == null) {
- perms = new ArraySet<>();
- }
- perms.add(perm);
- }
- } finally {
- sa.recycle();
- }
- XmlUtils.skipCurrentTag(parser);
- return perms;
- }
-
- private static ArraySet<String> parseAllowPermission(
- ArraySet<String> perms,
- Resources res,
- XmlResourceParser parser,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
- if (sa == null) {
- outError[0] = "<allow-permission> could not be parsed";
- return null;
- }
-
- try {
- String perm = sa.getNonConfigurationString(
- R.styleable.AndroidManifestAllowPermission_name,0);
- if (perm != null && perm.equals(android.Manifest.permission.INTERNET)
- && perms != null) {
- perms.remove(perm);
- if (perms.size() <= 0) {
- perms = null;
- }
- }
- } finally {
- sa.recycle();
- }
- XmlUtils.skipCurrentTag(parser);
- return perms;
- }
-
- public static ParsedProcess parseProcess(
- ArraySet<String> perms,
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- int flags,
- String[] outError
- ) throws IOException, XmlPullParserException {
- TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
- if (sa == null) {
- outError[0] = "<process> could not be parsed";
- return null;
- }
-
- ParsedProcess proc = new ParsedProcess();
- if (perms != null) {
- proc.deniedPermissions = new ArraySet(perms);
- }
-
- try {
- proc.name = sa.getNonConfigurationString(
- R.styleable.AndroidManifestProcess_process,0);
- proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
- parsingPackage.getPackageName(), proc.name, flags, separateProcesses, outError);
- if (outError[0] != null) {
- return null;
- }
- if (proc.name == null || proc.name.length() <= 0) {
- outError[0] = "<process> does not specify android:process";
- return null;
- }
- } finally {
- sa.recycle();
- }
-
- int type;
- final int innerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("deny-permission")) {
- proc.deniedPermissions = parseDenyPermission(proc.deniedPermissions, res, parser,
- outError);
- if (outError[0] != null) {
- return null;
- }
- } else if (tagName.equals("allow-permission")) {
- proc.deniedPermissions = parseAllowPermission(proc.deniedPermissions, res, parser,
- outError);
- if (outError[0] != null) {
- return null;
- }
- } else {
- Slog.w(TAG, "Unknown element under <process>: " + tagName
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- }
-
- return proc;
- }
-
- public static ArrayMap<String, ParsedProcess> parseProcesses(
- String[] separateProcesses,
- ParsingPackage parsingPackage,
- Resources res,
- XmlResourceParser parser,
- int flags,
- String[] outError
- ) throws IOException, XmlPullParserException {
- ArraySet<String> deniedPerms = null;
- ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
-
- int type;
- final int innerDepth = parser.getDepth();
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String tagName = parser.getName();
- if (tagName.equals("deny-permission")) {
- deniedPerms = parseDenyPermission(deniedPerms, res, parser, outError);
- if (outError[0] != null) {
- return null;
- }
- } else if (tagName.equals("allow-permission")) {
- deniedPerms = parseAllowPermission(deniedPerms, res, parser, outError);
- if (outError[0] != null) {
- return null;
- }
- } else if (tagName.equals("process")) {
- ParsedProcess proc = parseProcess(deniedPerms, separateProcesses, parsingPackage,
- res, parser, flags, outError);
- if (outError[0] != null) {
- return null;
- }
- if (processes.get(proc.name) != null) {
- outError[0] = "<process> specified existing name '" + proc.name + "'";
- return null;
- }
- processes.put(proc.name, proc);
- } else {
- Slog.w(TAG, "Unknown element under <processes>: " + tagName
- + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- }
- }
-
- return processes;
- }
-
- public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) {
- TypedArray sw = res.obtainAttributes(attrs,
- R.styleable.AndroidManifestLayout);
- int width = -1;
- float widthFraction = -1f;
- int height = -1;
- float heightFraction = -1f;
- final int widthType = sw.getType(
- R.styleable.AndroidManifestLayout_defaultWidth);
- if (widthType == TypedValue.TYPE_FRACTION) {
- widthFraction = sw.getFraction(
- R.styleable.AndroidManifestLayout_defaultWidth,
- 1, 1, -1);
- } else if (widthType == TypedValue.TYPE_DIMENSION) {
- width = sw.getDimensionPixelSize(
- R.styleable.AndroidManifestLayout_defaultWidth,
- -1);
- }
- final int heightType = sw.getType(
- R.styleable.AndroidManifestLayout_defaultHeight);
- if (heightType == TypedValue.TYPE_FRACTION) {
- heightFraction = sw.getFraction(
- R.styleable.AndroidManifestLayout_defaultHeight,
- 1, 1, -1);
- } else if (heightType == TypedValue.TYPE_DIMENSION) {
- height = sw.getDimensionPixelSize(
- R.styleable.AndroidManifestLayout_defaultHeight,
- -1);
- }
- int gravity = sw.getInt(
- R.styleable.AndroidManifestLayout_gravity,
- Gravity.CENTER);
- int minWidth = sw.getDimensionPixelSize(
- R.styleable.AndroidManifestLayout_minWidth,
- -1);
- int minHeight = sw.getDimensionPixelSize(
- R.styleable.AndroidManifestLayout_minHeight,
- -1);
- sw.recycle();
- return new ActivityInfo.WindowLayout(width, widthFraction,
- height, heightFraction, gravity, minWidth, minHeight);
- }
-
- public static boolean parseIntentInfo(
- ParsedIntentInfo intentInfo,
- ParsingPackage parsingPackage,
- Resources res, XmlResourceParser parser, boolean allowGlobs,
- boolean allowAutoVerify, String[] outError
- ) throws XmlPullParserException, IOException {
- TypedArray sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestIntentFilter);
-
- int priority = sa.getInt(
- R.styleable.AndroidManifestIntentFilter_priority, 0);
- intentInfo.setPriority(priority);
-
- int order = sa.getInt(
- R.styleable.AndroidManifestIntentFilter_order, 0);
- intentInfo.setOrder(order);
-
- TypedValue v = sa.peekValue(
- R.styleable.AndroidManifestIntentFilter_label);
- if (v != null && (intentInfo.labelRes = v.resourceId) == 0) {
- intentInfo.nonLocalizedLabel = v.coerceToString();
- }
-
- int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
- R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0;
- if (roundIconVal != 0) {
- intentInfo.icon = roundIconVal;
- } else {
- intentInfo.icon = sa.getResourceId(
- R.styleable.AndroidManifestIntentFilter_icon, 0);
- }
-
- if (allowAutoVerify) {
- intentInfo.setAutoVerify(sa.getBoolean(
- R.styleable.AndroidManifestIntentFilter_autoVerify,
- false));
- }
-
- sa.recycle();
-
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- String nodeName = parser.getName();
- if (nodeName.equals("action")) {
- String value = parser.getAttributeValue(
- PackageParser.ANDROID_RESOURCES, "name");
- if (TextUtils.isEmpty(value)) {
- outError[0] = "No value supplied for <android:name>";
- return false;
- }
- XmlUtils.skipCurrentTag(parser);
-
- intentInfo.addAction(value);
- } else if (nodeName.equals("category")) {
- String value = parser.getAttributeValue(
- PackageParser.ANDROID_RESOURCES, "name");
- if (TextUtils.isEmpty(value)) {
- outError[0] = "No value supplied for <android:name>";
- return false;
- }
- XmlUtils.skipCurrentTag(parser);
-
- intentInfo.addCategory(value);
-
- } else if (nodeName.equals("data")) {
- sa = res.obtainAttributes(parser,
- R.styleable.AndroidManifestData);
-
- String str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_mimeType, 0);
- if (str != null) {
- try {
- intentInfo.addRawDataType(str);
- } catch (IntentFilter.MalformedMimeTypeException e) {
- outError[0] = e.toString();
- sa.recycle();
- return false;
- }
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_mimeGroup, 0);
- if (str != null) {
- intentInfo.addMimeGroup(str);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_scheme, 0);
- if (str != null) {
- intentInfo.addDataScheme(str);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_ssp, 0);
- if (str != null) {
- intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_sspPrefix, 0);
- if (str != null) {
- intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_sspPattern, 0);
- if (str != null) {
- if (!allowGlobs) {
- outError[0] = "sspPattern not allowed here; ssp must be literal";
- return false;
- }
- intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
- }
-
- String host = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_host, 0);
- String port = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_port, 0);
- if (host != null) {
- intentInfo.addDataAuthority(host, port);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_path, 0);
- if (str != null) {
- intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_pathPrefix, 0);
- if (str != null) {
- intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_pathPattern, 0);
- if (str != null) {
- if (!allowGlobs) {
- outError[0] = "pathPattern not allowed here; path must be literal";
- return false;
- }
- intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
- }
-
- str = sa.getNonConfigurationString(
- R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
- if (str != null) {
- if (!allowGlobs) {
- outError[0] = "pathAdvancedPattern not allowed here; path must be literal";
- return false;
- }
- intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
- }
-
- sa.recycle();
- XmlUtils.skipCurrentTag(parser);
- } else if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under <intent-filter>: "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- } else {
- outError[0] = "Bad element under <intent-filter>: " + parser.getName();
- return false;
- }
- }
-
- intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
-
- if (PackageParser.DEBUG_PARSER) {
- final StringBuilder cats = new StringBuilder("Intent d=");
- cats.append(intentInfo.hasDefault);
- cats.append(", cat=");
-
- final Iterator<String> it = intentInfo.categoriesIterator();
- if (it != null) {
- while (it.hasNext()) {
- cats.append(' ');
- cats.append(it.next());
- }
- }
- Slog.d(TAG, cats.toString());
- }
-
- return true;
- }
-
- private static boolean parseAllMetaData(
- ParsingPackage parsingPackage,
- Resources res, XmlResourceParser parser, String tag,
- ParsedComponent outInfo,
- String[] outError
- ) throws XmlPullParserException, IOException {
- int outerDepth = parser.getDepth();
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
-
- if (parser.getName().equals("meta-data")) {
- if ((outInfo.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
- outInfo.metaData, outError)) == null) {
- return false;
- }
- } else {
- if (!PackageParser.RIGID_PARSER) {
- Slog.w(TAG, "Unknown element under " + tag + ": "
- + parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
- + parser.getPositionDescription());
- XmlUtils.skipCurrentTag(parser);
- continue;
- } else {
- outError[0] = "Bad element under " + tag + ": " + parser.getName();
- }
- }
- }
-
- return true;
- }
-
- public static boolean isImplicitlyExposedIntent(IntentFilter intent) {
- return intent.hasCategory(Intent.CATEGORY_BROWSABLE)
- || intent.hasAction(Intent.ACTION_SEND)
- || intent.hasAction(Intent.ACTION_SENDTO)
- || intent.hasAction(Intent.ACTION_SEND_MULTIPLE);
- }
-}
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
deleted file mode 100644
index d06865b..0000000
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ /dev/null
@@ -1,3379 +0,0 @@
-/*
- * Copyright (C) 2019 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.parsing;
-
-import static android.os.Build.VERSION_CODES.DONUT;
-
-import static java.util.Collections.emptyMap;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
-import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
-import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
-import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Parcel;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.CollectionUtils;
-import com.android.server.SystemConfig;
-
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-
-/**
- * The backing data for a package that was parsed from disk.
- *
- * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case
- * TODO(b/135203078): Field nullability annotations
- * TODO(b/135203078): Convert = 1 fields into Booleans
- * TODO(b/135203078): Make all lists nullable and Collections.unmodifiable immutable when returned.
- * Prefer add/set methods if adding is necessary.
- * TODO(b/135203078): Consider comments to disable auto-format and single-line, single-space all the
- * get/set methods to make this class far more compact. Maybe even separate some logic into parent
- * classes, assuming there is no overhead.
- * TODO(b/135203078): Copy documentation from PackageParser#Package for the relevant fields included
- * here. Should clarify and clean up any differences. Also consider renames if it helps make
- * things clearer.
- * TODO(b/135203078): Intern all possibl e String values? Initial refactor just mirrored old
- * behavior.
- *
- * @hide
- */
-public final class PackageImpl implements ParsingPackage, ParsedPackage, AndroidPackage,
- AndroidPackageWrite {
-
- private static final String TAG = "PackageImpl";
-
- // Resource boolean are -1, so 1 means we don't know the value.
- private int supportsSmallScreens = 1;
- private int supportsNormalScreens = 1;
- private int supportsLargeScreens = 1;
- private int supportsXLargeScreens = 1;
- private int resizeable = 1;
- private int anyDensity = 1;
-
- private long[] lastPackageUsageTimeInMills =
- new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
-
- private int versionCode;
- private int versionCodeMajor;
- private int baseRevisionCode;
- private String versionName;
-
- private boolean coreApp;
- private int compileSdkVersion;
- private String compileSdkVersionCodename;
-
- private String packageName;
- private String realPackage;
- private String manifestPackageName;
- private String baseCodePath;
-
- private boolean requiredForAllUsers;
- private String restrictedAccountType;
- private String requiredAccountType;
-
- private boolean baseHardwareAccelerated;
-
- private String overlayTarget;
- private String overlayTargetName;
- private String overlayCategory;
- private int overlayPriority;
- private boolean overlayIsStatic;
- private Map<String, String> overlayables = emptyMap();
-
- private String staticSharedLibName;
- private long staticSharedLibVersion;
- private ArrayList<String> libraryNames;
- private ArrayList<String> usesLibraries;
- private ArrayList<String> usesOptionalLibraries;
-
- private ArrayList<String> usesStaticLibraries;
- private long[] usesStaticLibrariesVersions;
- private String[][] usesStaticLibrariesCertDigests;
-
- private String sharedUserId;
-
- private int sharedUserLabel;
- private ArrayList<ConfigurationInfo> configPreferences;
- private ArrayList<FeatureInfo> reqFeatures;
- private ArrayList<FeatureGroupInfo> featureGroups;
-
- private byte[] restrictUpdateHash;
-
- private ArrayList<String> originalPackages;
- private ArrayList<String> adoptPermissions;
-
- private ArrayList<String> requestedPermissions;
- private ArrayList<String> implicitPermissions;
-
- private ArraySet<String> upgradeKeySets;
- private Map<String, ArraySet<PublicKey>> keySetMapping;
-
- private ArrayList<String> protectedBroadcasts;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedActivity> activities;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedActivity> receivers;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedService> services;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedProvider> providers;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedFeature> features;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedPermission> permissions;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedPermissionGroup> permissionGroups;
-
- @Nullable
- private ArrayList<ComponentParseUtils.ParsedInstrumentation> instrumentations;
-
- private ArrayList<ParsedActivityIntentInfo> preferredActivityFilters;
-
- private Bundle appMetaData;
-
- private String volumeUuid;
- private String applicationVolumeUuid;
- private PackageParser.SigningDetails signingDetails;
-
- private String codePath;
-
- private boolean use32BitAbi;
- private boolean visibleToInstantApps;
-
- private String cpuAbiOverride;
-
- private boolean isStub;
-
- // TODO(b/135203078): Remove, should be unused
- private int preferredOrder;
-
- private boolean forceQueryable;
-
- @Nullable
- private ArrayList<Intent> queriesIntents;
-
- @Nullable
- private ArrayList<String> queriesPackages;
-
- @Nullable
- private ArraySet<String> queriesProviders;
-
- @Nullable
- private ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;
-
- private String[] splitClassLoaderNames;
- private String[] splitCodePaths;
- private SparseArray<int[]> splitDependencies;
- private int[] splitFlags;
- private String[] splitNames;
- private int[] splitRevisionCodes;
-
- // TODO(b/135203078): Audit applicationInfo.something usages, which may be different from
- // package.something usages. There were differing cases of package.field = versus
- // package.appInfo.field =. This class assumes some obvious ones, like packageName,
- // were collapsible, but kept the following separate.
-
- private String applicationInfoBaseResourcePath;
- private String applicationInfoCodePath;
- private String applicationInfoResourcePath;
- private String[] applicationInfoSplitResourcePaths;
-
- private String appComponentFactory;
- private String backupAgentName;
- private int banner;
- private int category;
- private String classLoaderName;
- private String className;
- private int compatibleWidthLimitDp;
- private String credentialProtectedDataDir;
- private String dataDir;
- private int descriptionRes;
- private String deviceProtectedDataDir;
- private boolean enabled;
- private boolean crossProfile;
- private int flags;
- private int fullBackupContent;
- private boolean hiddenUntilInstalled;
- private int icon;
- private int iconRes;
- private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
- private int labelRes;
- private int largestWidthLimitDp;
- private int logo;
- private String manageSpaceActivityName;
- private float maxAspectRatio;
- private float minAspectRatio;
- private int minSdkVersion;
- private String name;
- private String nativeLibraryDir;
- private String nativeLibraryRootDir;
- private boolean nativeLibraryRootRequiresIsa;
- private int networkSecurityConfigRes;
- private CharSequence nonLocalizedLabel;
- private String permission;
- private String primaryCpuAbi;
- private int privateFlags;
- private String processName;
- private int requiresSmallestWidthDp;
- private int roundIconRes;
- private String secondaryCpuAbi;
- private String secondaryNativeLibraryDir;
- private String seInfo;
- private String seInfoUser;
- private int targetSandboxVersion;
- private int targetSdkVersion;
- private String taskAffinity;
- private int theme;
- private int uid = -1;
- private int uiOptions;
- private String[] usesLibraryFiles;
- private List<SharedLibraryInfo> usesLibraryInfos;
- private String zygotePreloadName;
- private boolean preserveLegacyExternalStorage;
-
- @Nullable
- private ArraySet<String> mimeGroups;
-
- @VisibleForTesting
- public PackageImpl(
- String packageName,
- String baseCodePath,
- TypedArray manifestArray,
- boolean isCoreApp
- ) {
- this.packageName = TextUtils.safeIntern(packageName);
- this.manifestPackageName = this.packageName;
- this.baseCodePath = baseCodePath;
-
- this.versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
- this.versionCodeMajor = manifestArray.getInteger(
- R.styleable.AndroidManifest_versionCodeMajor, 0);
- this.baseRevisionCode = manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode,
- 0);
- setVersionName(manifestArray.getNonConfigurationString(
- R.styleable.AndroidManifest_versionName, 0));
- this.coreApp = isCoreApp;
-
- this.compileSdkVersion = manifestArray.getInteger(
- R.styleable.AndroidManifest_compileSdkVersion, 0);
- setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
- R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
- }
-
- private PackageImpl(String packageName) {
- this.packageName = TextUtils.safeIntern(packageName);
- this.manifestPackageName = this.packageName;
- }
-
- @VisibleForTesting
- public static ParsingPackage forParsing(String packageName) {
- return new PackageImpl(packageName);
- }
-
- @VisibleForTesting
- public static ParsingPackage forParsing(
- String packageName,
- String baseCodePath,
- TypedArray manifestArray,
- boolean isCoreApp) {
- return new PackageImpl(packageName, baseCodePath, manifestArray, isCoreApp);
- }
-
- /**
- * Mock an unavailable {@link AndroidPackage} to use when removing a package from the system.
- * This can occur if the package was installed on a storage device that has since been removed.
- * Since the infrastructure uses {@link AndroidPackage}, but for this case only cares about
- * volumeUuid, just fake it rather than having separate method paths.
- */
- public static AndroidPackage buildFakeForDeletion(String packageName, String volumeUuid) {
- return new PackageImpl(packageName)
- .setVolumeUuid(volumeUuid)
- .hideAsParsed()
- .hideAsFinal();
- }
-
- @Override
- public ParsedPackage hideAsParsed() {
- return this;
- }
-
- @Override
- public AndroidPackage hideAsFinal() {
- updateFlags();
- return this;
- }
-
- @Override
- @Deprecated
- public AndroidPackageWrite mutate() {
- return this;
- }
-
- private void updateFlags() {
- if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- this.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
- }
- if (supportsNormalScreens != 0) {
- this.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
- }
- if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- this.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
- }
- if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.GINGERBREAD)) {
- this.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
- }
- if (resizeable < 0 || (resizeable > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- this.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
- }
- if (anyDensity < 0 || (anyDensity > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- this.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
- }
- }
-
- @Override
- public boolean usesCompatibilityMode() {
- int flags = 0;
-
- if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
- }
- if (supportsNormalScreens != 0) {
- flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
- }
- if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
- }
- if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.GINGERBREAD)) {
- flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
- }
- if (resizeable < 0 || (resizeable > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
- }
- if (anyDensity < 0 || (anyDensity > 0
- && targetSdkVersion
- >= Build.VERSION_CODES.DONUT)) {
- flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
- }
-
- return targetSdkVersion < DONUT
- || (flags & (ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
- | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
- | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)) == 0;
- }
-
- @Override
- public String getBaseCodePath() {
- return baseCodePath;
- }
-
- @Override
- public int getTargetSdkVersion() {
- return targetSdkVersion;
- }
-
- @Override
- public String getPackageName() {
- return packageName;
- }
-
- @Override
- public String getProcessName() {
- return processName;
- }
-
- @Override
- public String getPermission() {
- return permission;
- }
-
- @Override
- public String getStaticSharedLibName() {
- return staticSharedLibName;
- }
-
- @Override
- public long getStaticSharedLibVersion() {
- return staticSharedLibVersion;
- }
-
- @Override
- public String getSharedUserId() {
- return sharedUserId;
- }
-
- @Override
- public List<String> getRequestedPermissions() {
- return requestedPermissions == null ? Collections.emptyList() : requestedPermissions;
- }
-
- @Nullable
- @Override
- public List<ParsedInstrumentation> getInstrumentations() {
- return instrumentations;
- }
-
- @Override
- public Map<String, ArraySet<PublicKey>> getKeySetMapping() {
- return keySetMapping == null ? emptyMap() : keySetMapping;
- }
-
- @Override
- public float getMaxAspectRatio() {
- return maxAspectRatio;
- }
-
- @Override
- public float getMinAspectRatio() {
- return minAspectRatio;
- }
-
- @NonNull
- @Override
- public List<String> getLibraryNames() {
- return libraryNames == null ? Collections.emptyList() : libraryNames;
- }
-
- @Override
- public List<ParsedActivity> getActivities() {
- return activities == null ? Collections.emptyList()
- : activities;
- }
-
- @Override
- public Bundle getAppMetaData() {
- return appMetaData;
- }
-
- @Nullable
- @Override
- public List<String> getUsesLibraries() {
- return usesLibraries;
- }
-
- @Nullable
- @Override
- public List<String> getUsesStaticLibraries() {
- return usesStaticLibraries;
- }
-
- @Nullable
- @Override
- public ArrayMap<String, ComponentParseUtils.ParsedProcess> getProcesses() {
- return processes;
- }
-
- @Override
- public boolean isBaseHardwareAccelerated() {
- return baseHardwareAccelerated;
- }
-
- @Override
- public int getUiOptions() {
- return uiOptions;
- }
-
- // TODO(b/135203078): Checking flags directly can be error prone,
- // consider separate interface methods?
- @Override
- public int getFlags() {
- return flags;
- }
-
- // TODO(b/135203078): Checking flags directly can be error prone,
- // consider separate interface methods?
- @Override
- public int getPrivateFlags() {
- return privateFlags;
- }
-
- @Override
- public String getTaskAffinity() {
- return taskAffinity;
- }
-
- @Nullable
- @Override
- public List<String> getOriginalPackages() {
- return originalPackages;
- }
-
- @Override
- public PackageParser.SigningDetails getSigningDetails() {
- return signingDetails;
- }
-
- @Override
- public String getVolumeUuid() {
- return volumeUuid;
- }
-
- @Nullable
- @Override
- public List<ParsedPermissionGroup> getPermissionGroups() {
- return permissionGroups;
- }
-
- @Nullable
- @Override
- public List<ParsedPermission> getPermissions() {
- return permissions;
- }
-
- @Nullable
- @Override
- public List<ParsedFeature> getFeatures() {
- return features;
- }
-
- @Override
- public String getCpuAbiOverride() {
- return cpuAbiOverride;
- }
-
- @Override
- public String getPrimaryCpuAbi() {
- return primaryCpuAbi;
- }
-
- @Override
- public String getSecondaryCpuAbi() {
- return secondaryCpuAbi;
- }
-
- @Override
- public boolean isUse32BitAbi() {
- return use32BitAbi;
- }
-
- @Override
- public boolean isForceQueryable() {
- return forceQueryable;
- }
-
- @Override
- public String getCodePath() {
- return codePath;
- }
-
- @Override
- public String getNativeLibraryDir() {
- return nativeLibraryDir;
- }
-
- @Override
- public String getNativeLibraryRootDir() {
- return nativeLibraryRootDir;
- }
-
- @Override
- public boolean isNativeLibraryRootRequiresIsa() {
- return nativeLibraryRootRequiresIsa;
- }
-
- // TODO(b/135203078): Does nothing, remove?
- @Override
- public int getPreferredOrder() {
- return preferredOrder;
- }
-
- @Override
- public long getLongVersionCode() {
- return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
- }
-
- @Override
- public PackageImpl setIsOverlay(boolean isOverlay) {
- this.privateFlags = isOverlay
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY;
- return this;
- }
-
- @Override
- public PackageImpl setExternalStorage(boolean externalStorage) {
- this.flags = externalStorage
- ? this.flags | ApplicationInfo.FLAG_EXTERNAL_STORAGE
- : this.flags & ~ApplicationInfo.FLAG_EXTERNAL_STORAGE;
- return this;
- }
-
- @Override
- public PackageImpl setIsolatedSplitLoading(boolean isolatedSplitLoading) {
- this.privateFlags = isolatedSplitLoading
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
- return this;
- }
-
- @Override
- public PackageImpl sortActivities() {
- Collections.sort(this.activities, (a1, a2) -> Integer.compare(a2.order, a1.order));
- return this;
- }
-
- @Override
- public PackageImpl sortReceivers() {
- Collections.sort(this.receivers, (a1, a2) -> Integer.compare(a2.order, a1.order));
- return this;
- }
-
- @Override
- public PackageImpl sortServices() {
- Collections.sort(this.services, (a1, a2) -> Integer.compare(a2.order, a1.order));
- return this;
- }
-
- @Override
- public PackageImpl setBaseRevisionCode(int baseRevisionCode) {
- this.baseRevisionCode = baseRevisionCode;
- return this;
- }
-
- @Override
- public PackageImpl setPreferredOrder(int preferredOrder) {
- this.preferredOrder = preferredOrder;
- return this;
- }
-
- @Override
- public PackageImpl setVersionName(String versionName) {
- this.versionName = TextUtils.safeIntern(versionName);
- return this;
- }
-
- @Override
- public ParsingPackage setCompileSdkVersion(int compileSdkVersion) {
- this.compileSdkVersion = compileSdkVersion;
- return this;
- }
-
- @Override
- public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) {
- this.compileSdkVersionCodename = TextUtils.safeIntern(compileSdkVersionCodename);
- return this;
- }
-
- @Override
- public PackageImpl setMaxAspectRatio(float maxAspectRatio) {
- this.maxAspectRatio = maxAspectRatio;
- return this;
- }
-
- @Override
- public PackageImpl setMinAspectRatio(float minAspectRatio) {
- this.minAspectRatio = minAspectRatio;
- return this;
- }
-
- @Override
- public PackageImpl setMinSdkVersion(int minSdkVersion) {
- this.minSdkVersion = minSdkVersion;
- return this;
- }
-
- @Override
- public PackageImpl setTargetSdkVersion(int targetSdkVersion) {
- this.targetSdkVersion = targetSdkVersion;
- return this;
- }
-
- @Override
- public PackageImpl setRealPackage(String realPackage) {
- this.realPackage = realPackage;
- return this;
- }
-
- @Override
- public PackageImpl addConfigPreference(ConfigurationInfo configPreference) {
- this.configPreferences = ArrayUtils.add(this.configPreferences, configPreference);
- return this;
- }
-
- @Override
- public PackageImpl addReqFeature(FeatureInfo reqFeature) {
- this.reqFeatures = ArrayUtils.add(this.reqFeatures, reqFeature);
- return this;
- }
-
- @Override
- public PackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) {
- this.featureGroups = ArrayUtils.add(this.featureGroups, featureGroup);
- return this;
- }
-
- @Override
- public PackageImpl addProtectedBroadcast(String protectedBroadcast) {
- if (this.protectedBroadcasts == null
- || !this.protectedBroadcasts.contains(protectedBroadcast)) {
- this.protectedBroadcasts = ArrayUtils.add(this.protectedBroadcasts,
- TextUtils.safeIntern(protectedBroadcast));
- }
- return this;
- }
-
- @Override
- public PackageImpl addInstrumentation(ParsedInstrumentation instrumentation) {
- this.instrumentations = ArrayUtils.add(this.instrumentations, instrumentation);
- return this;
- }
-
- @Override
- public PackageImpl addOriginalPackage(String originalPackage) {
- this.originalPackages = ArrayUtils.add(this.originalPackages, originalPackage);
- return this;
- }
-
- @Override
- public ParsingPackage addOverlayable(String overlayableName, String actorName) {
- this.overlayables = CollectionUtils.add(this.overlayables,
- TextUtils.safeIntern(overlayableName), TextUtils.safeIntern(actorName));
- return this;
- }
-
- @Override
- public PackageImpl addAdoptPermission(String adoptPermission) {
- this.adoptPermissions = ArrayUtils.add(this.adoptPermissions, adoptPermission);
- return this;
- }
-
- @Override
- public PackageImpl addFeature(ParsedFeature feature) {
- this.features = ArrayUtils.add(this.features, feature);
- return this;
- }
-
- @Override
- public PackageImpl addPermission(ParsedPermission permission) {
- this.permissions = ArrayUtils.add(this.permissions, permission);
- return this;
- }
-
- @Override
- public PackageImpl removePermission(int index) {
- this.permissions.remove(index);
- return this;
- }
-
- @Override
- public PackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) {
- this.permissionGroups = ArrayUtils.add(this.permissionGroups, permissionGroup);
- return this;
- }
-
- @Override
- public PackageImpl addRequestedPermission(String permission) {
- this.requestedPermissions = ArrayUtils.add(this.requestedPermissions,
- TextUtils.safeIntern(permission));
- return this;
- }
-
- @Override
- public PackageImpl addImplicitPermission(String permission) {
- this.implicitPermissions = ArrayUtils.add(this.implicitPermissions,
- TextUtils.safeIntern(permission));
- return this;
- }
-
- @Override
- public PackageImpl addKeySet(String keySetName, PublicKey publicKey) {
- if (keySetMapping == null) {
- keySetMapping = new ArrayMap<>();
- }
-
- ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName);
- if (publicKeys == null) {
- publicKeys = new ArraySet<>();
- keySetMapping.put(keySetName, publicKeys);
- }
-
- publicKeys.add(publicKey);
-
- return this;
- }
-
- @Override
- public ParsingPackage addActivity(ParsedActivity parsedActivity) {
- this.activities = ArrayUtils.add(this.activities, parsedActivity);
- addMimeGroupsFromComponent(parsedActivity);
- return this;
- }
-
- @Override
- public ParsingPackage addReceiver(ParsedActivity parsedReceiver) {
- this.receivers = ArrayUtils.add(this.receivers, parsedReceiver);
- addMimeGroupsFromComponent(parsedReceiver);
- return this;
- }
-
- @Override
- public ParsingPackage addService(ParsedService parsedService) {
- this.services = ArrayUtils.add(this.services, parsedService);
- addMimeGroupsFromComponent(parsedService);
- return this;
- }
-
- @Override
- public ParsingPackage addProvider(ParsedProvider parsedProvider) {
- this.providers = ArrayUtils.add(this.providers, parsedProvider);
- addMimeGroupsFromComponent(parsedProvider);
- return this;
- }
-
- @Override
- public PackageImpl addLibraryName(String libraryName) {
- this.libraryNames = ArrayUtils.add(this.libraryNames, TextUtils.safeIntern(libraryName));
- return this;
- }
-
- @Override
- public PackageImpl addUsesLibrary(String libraryName) {
- this.usesLibraries = ArrayUtils.add(this.usesLibraries, TextUtils.safeIntern(libraryName));
- return this;
- }
-
- @Override
- public PackageImpl addUsesOptionalLibrary(String libraryName) {
- this.usesOptionalLibraries = ArrayUtils.add(this.usesOptionalLibraries,
- TextUtils.safeIntern(libraryName));
- return this;
- }
-
- @Override
- public PackageImpl removeUsesOptionalLibrary(String libraryName) {
- this.usesOptionalLibraries = ArrayUtils.remove(this.usesOptionalLibraries, libraryName);
- return this;
- }
-
- @Override
- public PackageImpl addUsesStaticLibrary(String libraryName) {
- this.usesStaticLibraries = ArrayUtils.add(this.usesStaticLibraries,
- TextUtils.safeIntern(libraryName));
- return this;
- }
-
- @Override
- public PackageImpl addUsesStaticLibraryVersion(long version) {
- this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
- version, true);
- return this;
- }
-
- @Override
- public PackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) {
- this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
- this.usesStaticLibrariesCertDigests, certSha256Digests, true);
- return this;
- }
-
- @Override
- public PackageImpl addPreferredActivityFilter(
- ParsedActivityIntentInfo parsedActivityIntentInfo) {
- this.preferredActivityFilters = ArrayUtils.add(this.preferredActivityFilters,
- parsedActivityIntentInfo);
- return this;
- }
-
- @Override
- public PackageImpl addQueriesIntent(Intent intent) {
- this.queriesIntents = ArrayUtils.add(this.queriesIntents, intent);
- return this;
- }
-
- @Override
- public PackageImpl addQueriesPackage(String packageName) {
- this.queriesPackages = ArrayUtils.add(this.queriesPackages,
- TextUtils.safeIntern(packageName));
- return this;
- }
-
- @Override
- public ParsingPackage addQueriesProvider(String authority) {
- this.queriesProviders = ArrayUtils.add(this.queriesProviders, authority);
- return this;
- }
-
- @Override
- public PackageImpl setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes) {
- this.processes = processes;
- return this;
- }
-
- @Override
- public PackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
- if (supportsSmallScreens == 1) {
- return this;
- }
-
- this.supportsSmallScreens = supportsSmallScreens;
- return this;
- }
-
- @Override
- public PackageImpl setSupportsNormalScreens(int supportsNormalScreens) {
- if (supportsNormalScreens == 1) {
- return this;
- }
-
- this.supportsNormalScreens = supportsNormalScreens;
- return this;
- }
-
- @Override
- public PackageImpl setSupportsLargeScreens(int supportsLargeScreens) {
- if (supportsLargeScreens == 1) {
- return this;
- }
-
- this.supportsLargeScreens = supportsLargeScreens;
- return this;
- }
-
- @Override
- public PackageImpl setSupportsXLargeScreens(int supportsXLargeScreens) {
- if (supportsXLargeScreens == 1) {
- return this;
- }
-
- this.supportsXLargeScreens = supportsXLargeScreens;
- return this;
- }
-
- @Override
- public PackageImpl setResizeable(int resizeable) {
- if (resizeable == 1) {
- return this;
- }
-
- this.resizeable = resizeable;
- return this;
- }
-
- @Override
- public PackageImpl setAnyDensity(int anyDensity) {
- if (anyDensity == 1) {
- return this;
- }
-
- this.anyDensity = anyDensity;
- return this;
- }
-
- @Override
- public PackageImpl setRequiresSmallestWidthDp(int requiresSmallestWidthDp) {
- this.requiresSmallestWidthDp = requiresSmallestWidthDp;
- return this;
- }
-
- @Override
- public PackageImpl setCompatibleWidthLimitDp(int compatibleWidthLimitDp) {
- this.compatibleWidthLimitDp = compatibleWidthLimitDp;
- return this;
- }
-
- @Override
- public PackageImpl setLargestWidthLimitDp(int largestWidthLimitDp) {
- this.largestWidthLimitDp = largestWidthLimitDp;
- return this;
- }
-
- @Override
- public PackageImpl setInstallLocation(int installLocation) {
- this.installLocation = installLocation;
- return this;
- }
-
- @Override
- public PackageImpl setTargetSandboxVersion(int targetSandboxVersion) {
- this.targetSandboxVersion = targetSandboxVersion;
- return this;
- }
-
- @Override
- public PackageImpl setRequiredForAllUsers(boolean requiredForAllUsers) {
- this.requiredForAllUsers = requiredForAllUsers;
- return this;
- }
-
- @Override
- public PackageImpl setRestrictedAccountType(String restrictedAccountType) {
- this.restrictedAccountType = restrictedAccountType;
- return this;
- }
-
- @Override
- public PackageImpl setRequiredAccountType(String requiredAccountType) {
- this.requiredAccountType = requiredAccountType;
- return this;
- }
-
- @Override
- public PackageImpl setBaseHardwareAccelerated(boolean baseHardwareAccelerated) {
- this.baseHardwareAccelerated = baseHardwareAccelerated;
-
- this.flags = baseHardwareAccelerated
- ? this.flags | ApplicationInfo.FLAG_HARDWARE_ACCELERATED
- : this.flags & ~ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
-
- return this;
- }
-
- @Override
- public PackageImpl setHasDomainUrls(boolean hasDomainUrls) {
- this.privateFlags = hasDomainUrls
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
- return this;
- }
-
- @Override
- public PackageImpl setAppMetaData(Bundle appMetaData) {
- this.appMetaData = appMetaData;
- return this;
- }
-
- @Override
- public PackageImpl setOverlayTarget(String overlayTarget) {
- this.overlayTarget = overlayTarget;
- return this;
- }
-
- @Override
- public PackageImpl setOverlayTargetName(String overlayTargetName) {
- this.overlayTargetName = overlayTargetName;
- return this;
- }
-
- @Override
- public PackageImpl setOverlayCategory(String overlayCategory) {
- this.overlayCategory = overlayCategory;
- return this;
- }
-
- @Override
- public PackageImpl setOverlayPriority(int overlayPriority) {
- this.overlayPriority = overlayPriority;
- return this;
- }
-
- @Override
- public PackageImpl setOverlayIsStatic(boolean overlayIsStatic) {
- this.overlayIsStatic = overlayIsStatic;
- return this;
- }
-
- @Override
- public PackageImpl setStaticSharedLibName(String staticSharedLibName) {
- this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
- return this;
- }
-
- @Override
- public PackageImpl setStaticSharedLibVersion(long staticSharedLibVersion) {
- this.staticSharedLibVersion = staticSharedLibVersion;
- return this;
- }
-
- @Override
- public PackageImpl setSharedUserId(String sharedUserId) {
- this.sharedUserId = TextUtils.safeIntern(sharedUserId);
- return this;
- }
-
- @Override
- public PackageImpl setSharedUserLabel(int sharedUserLabel) {
- this.sharedUserLabel = sharedUserLabel;
- return this;
- }
-
- @Override
- public PackageImpl setRestrictUpdateHash(byte[] restrictUpdateHash) {
- this.restrictUpdateHash = restrictUpdateHash;
- return this;
- }
-
- @Override
- public PackageImpl setUpgradeKeySets(ArraySet<String> upgradeKeySets) {
- this.upgradeKeySets = upgradeKeySets;
- return this;
- }
-
- @Override
- public PackageImpl setVolumeUuid(String volumeUuid) {
- this.volumeUuid = volumeUuid;
- return this;
- }
-
- @Deprecated
- @Override
- public PackageImpl setApplicationVolumeUuid(String applicationVolumeUuid) {
- this.applicationVolumeUuid = applicationVolumeUuid;
- return this;
- }
-
- @Override
- public PackageImpl setSigningDetails(PackageParser.SigningDetails signingDetails) {
- this.signingDetails = signingDetails;
- return this;
- }
-
- @Override
- public PackageImpl setCodePath(String codePath) {
- this.codePath = codePath;
- return this;
- }
-
- @Override
- public PackageImpl setUse32BitAbi(boolean use32BitAbi) {
- this.use32BitAbi = use32BitAbi;
- return this;
- }
-
- @Override
- public PackageImpl setCpuAbiOverride(String cpuAbiOverride) {
- this.cpuAbiOverride = cpuAbiOverride;
- return this;
- }
-
- @Override
- public PackageImpl setForceQueryable(boolean forceQueryable) {
- this.forceQueryable = forceQueryable;
- return this;
- }
-
- // TODO(b/135203078): Remove and move PackageManagerService#renameStaticSharedLibraryPackage
- // into initial package parsing
- @Override
- public PackageImpl setPackageName(String packageName) {
- this.packageName = packageName.intern();
-
- if (permissions != null) {
- for (ParsedPermission permission : permissions) {
- permission.setPackageName(this.packageName);
- }
- }
-
- if (permissionGroups != null) {
- for (ParsedPermissionGroup permissionGroup : permissionGroups) {
- permissionGroup.setPackageName(this.packageName);
- }
- }
-
- if (activities != null) {
- for (ParsedActivity parsedActivity : activities) {
- parsedActivity.setPackageName(this.packageName);
- }
- }
-
- if (receivers != null) {
- for (ParsedActivity receiver : receivers) {
- receiver.setPackageName(this.packageName);
- }
- }
-
- if (providers != null) {
- for (ParsedProvider provider : providers) {
- provider.setPackageName(this.packageName);
- }
- }
-
- if (services != null) {
- for (ParsedService service : services) {
- service.setPackageName(this.packageName);
- }
- }
-
- if (instrumentations != null) {
- for (ParsedInstrumentation instrumentation : instrumentations) {
- instrumentation.setPackageName(this.packageName);
- }
- }
-
- return this;
- }
-
- // Under this is parseBaseApplication
-
- @Override
- public PackageImpl setAllowBackup(boolean allowBackup) {
- this.flags = allowBackup
- ? this.flags | ApplicationInfo.FLAG_ALLOW_BACKUP
- : this.flags & ~ApplicationInfo.FLAG_ALLOW_BACKUP;
- return this;
- }
-
- @Override
- public PackageImpl setKillAfterRestore(boolean killAfterRestore) {
- this.flags = killAfterRestore
- ? this.flags | ApplicationInfo.FLAG_KILL_AFTER_RESTORE
- : this.flags & ~ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
- return this;
- }
-
- @Override
- public PackageImpl setRestoreAnyVersion(boolean restoreAnyVersion) {
- this.flags = restoreAnyVersion
- ? this.flags | ApplicationInfo.FLAG_RESTORE_ANY_VERSION
- : this.flags & ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
- return this;
- }
-
- @Override
- public PackageImpl setFullBackupOnly(boolean fullBackupOnly) {
- this.flags = fullBackupOnly
- ? this.flags | ApplicationInfo.FLAG_FULL_BACKUP_ONLY
- : this.flags & ~ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
- return this;
- }
-
- @Override
- public PackageImpl setPersistent(boolean persistent) {
- this.flags = persistent
- ? this.flags | ApplicationInfo.FLAG_PERSISTENT
- : this.flags & ~ApplicationInfo.FLAG_PERSISTENT;
- return this;
- }
-
- @Override
- public PackageImpl setDebuggable(boolean debuggable) {
- this.flags = debuggable
- ? this.flags | ApplicationInfo.FLAG_DEBUGGABLE
- : this.flags & ~ApplicationInfo.FLAG_DEBUGGABLE;
- return this;
- }
-
- @Override
- public PackageImpl setProfileableByShell(boolean profileableByShell) {
- this.privateFlags = profileableByShell
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL;
- return this;
- }
-
- @Override
- public PackageImpl setVmSafeMode(boolean vmSafeMode) {
- this.flags = vmSafeMode
- ? this.flags | ApplicationInfo.FLAG_VM_SAFE_MODE
- : this.flags & ~ApplicationInfo.FLAG_VM_SAFE_MODE;
- return this;
- }
-
- @Override
- public PackageImpl setHasCode(boolean hasCode) {
- this.flags = hasCode
- ? this.flags | ApplicationInfo.FLAG_HAS_CODE
- : this.flags & ~ApplicationInfo.FLAG_HAS_CODE;
- return this;
- }
-
- @Override
- public PackageImpl setAllowTaskReparenting(boolean allowTaskReparenting) {
- this.flags = allowTaskReparenting
- ? this.flags | ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING
- : this.flags & ~ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
- return this;
- }
-
- @Override
- public PackageImpl setAllowClearUserData(boolean allowClearUserData) {
- this.flags = allowClearUserData
- ? this.flags | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA
- : this.flags & ~ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
- return this;
- }
-
- @Override
- public PackageImpl setLargeHeap(boolean largeHeap) {
- this.flags = largeHeap
- ? this.flags | ApplicationInfo.FLAG_LARGE_HEAP
- : this.flags & ~ApplicationInfo.FLAG_LARGE_HEAP;
- return this;
- }
-
- @Override
- public PackageImpl setUsesCleartextTraffic(boolean usesCleartextTraffic) {
- this.flags = usesCleartextTraffic
- ? this.flags | ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC
- : this.flags & ~ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC;
- return this;
- }
-
- @Override
- public PackageImpl setSupportsRtl(boolean supportsRtl) {
- this.flags = supportsRtl
- ? this.flags | ApplicationInfo.FLAG_SUPPORTS_RTL
- : this.flags & ~ApplicationInfo.FLAG_SUPPORTS_RTL;
- return this;
- }
-
- @Override
- public PackageImpl setTestOnly(boolean testOnly) {
- this.flags = testOnly
- ? this.flags | ApplicationInfo.FLAG_TEST_ONLY
- : this.flags & ~ApplicationInfo.FLAG_TEST_ONLY;
- return this;
- }
-
- @Override
- public PackageImpl setMultiArch(boolean multiArch) {
- this.flags = multiArch
- ? this.flags | ApplicationInfo.FLAG_MULTIARCH
- : this.flags & ~ApplicationInfo.FLAG_MULTIARCH;
- return this;
- }
-
- @Override
- public PackageImpl setExtractNativeLibs(boolean extractNativeLibs) {
- this.flags = extractNativeLibs
- ? this.flags | ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS
- : this.flags & ~ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
- return this;
- }
-
- @Override
- public PackageImpl setIsGame(boolean isGame) {
- this.flags = isGame
- ? this.flags | ApplicationInfo.FLAG_IS_GAME
- : this.flags & ~ApplicationInfo.FLAG_IS_GAME;
- return this;
- }
-
- @Override
- public PackageImpl setBackupInForeground(boolean backupInForeground) {
- this.privateFlags = backupInForeground
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
- return this;
- }
-
- @Override
- public PackageImpl setUseEmbeddedDex(boolean useEmbeddedDex) {
- this.privateFlags = useEmbeddedDex
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX;
- return this;
- }
-
- @Override
- public PackageImpl setDefaultToDeviceProtectedStorage(boolean defaultToDeviceProtectedStorage) {
- this.privateFlags = defaultToDeviceProtectedStorage
- ? this.privateFlags | ApplicationInfo
- .PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE
- : this.privateFlags & ~ApplicationInfo
- .PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
- return this;
- }
-
- @Override
- public PackageImpl setDirectBootAware(boolean directBootAware) {
- this.privateFlags = directBootAware
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
- return this;
- }
-
- @Override
- public PackageImpl setPartiallyDirectBootAware(boolean partiallyDirectBootAware) {
- this.privateFlags = partiallyDirectBootAware
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
- return this;
- }
-
- @Override
- public PackageImpl setActivitiesResizeModeResizeableViaSdkVersion(
- boolean resizeableViaSdkVersion
- ) {
- this.privateFlags = resizeableViaSdkVersion
- ? this.privateFlags | ApplicationInfo
- .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
- : this.privateFlags & ~ApplicationInfo
- .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
- return this;
- }
-
- @Override
- public PackageImpl setActivitiesResizeModeResizeable(boolean resizeable) {
- this.privateFlags = resizeable
- ? this.privateFlags | ApplicationInfo
- .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
- : this.privateFlags & ~ApplicationInfo
- .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
-
- this.privateFlags = !resizeable
- ? this.privateFlags | ApplicationInfo
- .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE
- : this.privateFlags & ~ApplicationInfo
- .PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
- return this;
- }
-
- @Override
- public PackageImpl setAllowClearUserDataOnFailedRestore(
- boolean allowClearUserDataOnFailedRestore
- ) {
- this.privateFlags = allowClearUserDataOnFailedRestore
- ? this.privateFlags | ApplicationInfo
- .PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
- : this.privateFlags & ~ApplicationInfo
- .PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE;
- return this;
- }
-
- @Override
- public PackageImpl setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture) {
- this.privateFlags = allowAudioPlaybackCapture
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE;
- return this;
- }
-
- @Override
- public PackageImpl setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage) {
- this.privateFlags = requestLegacyExternalStorage
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE;
- return this;
- }
-
- @Override
- public PackageImpl setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging) {
- this.privateFlags = allowNativeHeapPointerTagging
- ? this.privateFlags | ApplicationInfo
- .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
- : this.privateFlags & ~ApplicationInfo
- .PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING;
- return this;
- }
-
- @Override
- public PackageImpl setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage) {
- this.preserveLegacyExternalStorage = preserveLegacyExternalStorage;
- return this;
- }
-
- @Override
- public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
- this.privateFlags = usesNonSdkApi
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
- return this;
- }
-
- @Override
- public PackageImpl setHasFragileUserData(boolean hasFragileUserData) {
- this.privateFlags = hasFragileUserData
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA;
- return this;
- }
-
- @Override
- public PackageImpl setCantSaveState(boolean cantSaveState) {
- this.privateFlags = cantSaveState
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
- return this;
- }
-
- @Override
- public boolean cantSaveState() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0;
- }
-
- @Override
- public boolean isLibrary() {
- return staticSharedLibName != null || !ArrayUtils.isEmpty(libraryNames);
- }
-
- // TODO(b/135203078): This does nothing until the final stage without applyPolicy being
- // part of PackageParser
- @Override
- public boolean isSystemApp() {
- return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- }
-
- // TODO(b/135203078): This does nothing until the final stage without applyPolicy being
- // part of PackageParser
- @Override
- public boolean isSystemExt() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
- }
-
- // TODO(b/135203078): This does nothing until the final stage without applyPolicy being
- // part of PackageParser
- @Override
- public boolean isUpdatedSystemApp() {
- return (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
- }
-
- @Override
- public PackageImpl setStaticSharedLibrary(boolean staticSharedLibrary) {
- this.privateFlags = staticSharedLibrary
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
- return this;
- }
-
- @Override
- public boolean isStaticSharedLibrary() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) != 0;
- }
-
- @Override
- public PackageImpl setVisibleToInstantApps(boolean visibleToInstantApps) {
- this.visibleToInstantApps = visibleToInstantApps;
- return this;
- }
-
- @Override
- public PackageImpl setIconRes(int iconRes) {
- this.iconRes = iconRes;
- return this;
- }
-
- @Override
- public PackageImpl setRoundIconRes(int roundIconRes) {
- this.roundIconRes = roundIconRes;
- return this;
- }
-
- @Override
- public PackageImpl setClassName(String className) {
- this.className = className;
- return this;
- }
-
- @Override
- public PackageImpl setManageSpaceActivityName(String manageSpaceActivityName) {
- this.manageSpaceActivityName = manageSpaceActivityName;
- return this;
- }
-
- @Override
- public PackageImpl setBackupAgentName(String backupAgentName) {
- this.backupAgentName = backupAgentName;
- return this;
- }
-
- @Override
- public PackageImpl setFullBackupContent(int fullBackupContent) {
- this.fullBackupContent = fullBackupContent;
- return this;
- }
-
- @Override
- public PackageImpl setTheme(int theme) {
- this.theme = theme;
- return this;
- }
-
- @Override
- public PackageImpl setDescriptionRes(int descriptionRes) {
- this.descriptionRes = descriptionRes;
- return this;
- }
-
- @Override
- public PackageImpl setNetworkSecurityConfigRes(int networkSecurityConfigRes) {
- this.networkSecurityConfigRes = networkSecurityConfigRes;
- return this;
- }
-
- @Override
- public PackageImpl setCategory(int category) {
- this.category = category;
- return this;
- }
-
- @Override
- public PackageImpl setPermission(String permission) {
- this.permission = permission;
- return this;
- }
-
- @Override
- public PackageImpl setTaskAffinity(String taskAffinity) {
- this.taskAffinity = taskAffinity;
- return this;
- }
-
- @Override
- public PackageImpl setAppComponentFactory(String appComponentFactory) {
- this.appComponentFactory = appComponentFactory;
- return this;
- }
-
- @Override
- public PackageImpl setProcessName(String processName) {
- if (processName == null) {
- this.processName = packageName;
- } else {
- this.processName = processName;
- }
- return this;
- }
-
- @Override
- public PackageImpl setEnabled(boolean enabled) {
- this.enabled = enabled;
- return this;
- }
-
- @Override
- public PackageImpl setCrossProfile(boolean crossProfile) {
- this.crossProfile = crossProfile;
- return this;
- }
-
- @Override
- public PackageImpl setUiOptions(int uiOptions) {
- this.uiOptions = uiOptions;
- return this;
- }
-
- @Override
- public PackageImpl setClassLoaderName(String classLoaderName) {
- this.classLoaderName = classLoaderName;
- return this;
- }
-
- @Override
- public PackageImpl setZygotePreloadName(String zygotePreloadName) {
- this.zygotePreloadName = zygotePreloadName;
- return this;
- }
-
- // parsePackageItemInfo
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public PackageImpl setName(String name) {
- this.name = name;
- return this;
- }
-
- @Override
- public PackageImpl setIcon(int icon) {
- this.icon = icon;
- return this;
- }
-
- @Override
- public PackageImpl setNonLocalizedLabel(CharSequence nonLocalizedLabel) {
- this.nonLocalizedLabel = nonLocalizedLabel;
- return this;
- }
-
- @Override
- public PackageImpl setLogo(int logo) {
- this.logo = logo;
- return this;
- }
-
- @Override
- public PackageImpl setBanner(int banner) {
- this.banner = banner;
- return this;
- }
-
- @Override
- public PackageImpl setLabelRes(int labelRes) {
- this.labelRes = labelRes;
- return this;
- }
-
- @Override
- public PackageImpl asSplit(
- String[] splitNames,
- String[] splitCodePaths,
- int[] splitRevisionCodes,
- SparseArray<int[]> splitDependencies
- ) {
- this.splitNames = splitNames;
-
- if (this.splitNames != null) {
- for (int index = 0; index < this.splitNames.length; index++) {
- splitNames[index] = TextUtils.safeIntern(splitNames[index]);
- }
- }
-
- this.splitCodePaths = splitCodePaths;
- this.splitRevisionCodes = splitRevisionCodes;
- this.splitDependencies = splitDependencies;
-
- int count = splitNames.length;
- this.splitFlags = new int[count];
- this.splitClassLoaderNames = new String[count];
- return this;
- }
-
- @Override
- public String[] getSplitNames() {
- return splitNames;
- }
-
- @Override
- public String[] getSplitCodePaths() {
- return splitCodePaths;
- }
-
- @Override
- public PackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) {
- this.splitFlags[splitIndex] = splitHasCode
- ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE
- : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE;
- return this;
- }
-
- @Override
- public PackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) {
- this.splitClassLoaderNames[splitIndex] = classLoaderName;
- return this;
- }
-
- @Override
- public List<String> makeListAllCodePaths() {
- ArrayList<String> paths = new ArrayList<>();
- paths.add(baseCodePath);
-
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- Collections.addAll(paths, splitCodePaths);
- }
- return paths;
- }
-
- @Override
- public PackageImpl setBaseCodePath(String baseCodePath) {
- this.baseCodePath = baseCodePath;
- return this;
- }
-
- @Override
- public PackageImpl setSplitCodePaths(String[] splitCodePaths) {
- this.splitCodePaths = splitCodePaths;
- return this;
- }
-
- @Override
- public String toString() {
- return "Package{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + packageName + "}";
- }
-
- @Override
- public PackageImpl setPrimaryCpuAbi(String primaryCpuAbi) {
- this.primaryCpuAbi = primaryCpuAbi;
- return this;
- }
-
- @Override
- public PackageImpl setSecondaryCpuAbi(String secondaryCpuAbi) {
- this.secondaryCpuAbi = secondaryCpuAbi;
- return this;
- }
-
- @Override
- public PackageImpl setNativeLibraryRootDir(String nativeLibraryRootDir) {
- this.nativeLibraryRootDir = nativeLibraryRootDir;
- return this;
- }
-
- @Override
- public PackageImpl setNativeLibraryRootRequiresIsa(boolean nativeLibraryRootRequiresIsa) {
- this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
- return this;
- }
-
- @Override
- public PackageImpl setNativeLibraryDir(String nativeLibraryDir) {
- this.nativeLibraryDir = nativeLibraryDir;
- return this;
- }
-
- @Override
- public PackageImpl setSecondaryNativeLibraryDir(String secondaryNativeLibraryDir) {
- this.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
- return this;
- }
-
- @Deprecated
- @Override
- public PackageImpl setApplicationInfoCodePath(String applicationInfoCodePath) {
- this.applicationInfoCodePath = applicationInfoCodePath;
- return this;
- }
-
- @Deprecated
- @Override
- public PackageImpl setApplicationInfoResourcePath(String applicationInfoResourcePath) {
- this.applicationInfoResourcePath = applicationInfoResourcePath;
- return this;
- }
-
- @Deprecated
- @Override
- public PackageImpl setApplicationInfoBaseResourcePath(
- String applicationInfoBaseResourcePath) {
- this.applicationInfoBaseResourcePath = applicationInfoBaseResourcePath;
- return this;
- }
-
- @Deprecated
- @Override
- public PackageImpl setApplicationInfoSplitResourcePaths(
- String[] applicationInfoSplitResourcePaths) {
- this.applicationInfoSplitResourcePaths = applicationInfoSplitResourcePaths;
- return this;
- }
-
- @Override
- public boolean isDirectBootAware() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) != 0;
- }
-
- @Override
- public PackageImpl setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware) {
- if (activities != null) {
- for (ParsedActivity parsedActivity : activities) {
- parsedActivity.directBootAware = allComponentsDirectBootAware;
- }
- }
-
- if (receivers != null) {
- for (ParsedActivity parsedReceiver : receivers) {
- parsedReceiver.directBootAware = allComponentsDirectBootAware;
- }
- }
-
- if (providers != null) {
- for (ParsedProvider parsedProvider : providers) {
- parsedProvider.directBootAware = allComponentsDirectBootAware;
- }
- }
-
- if (services != null) {
- for (ParsedService parsedService : services) {
- parsedService.directBootAware = allComponentsDirectBootAware;
- }
- }
-
- return this;
- }
-
- @Override
- public PackageImpl setSystem(boolean system) {
- this.flags = system
- ? this.flags | ApplicationInfo.FLAG_SYSTEM
- : this.flags & ~ApplicationInfo.FLAG_SYSTEM;
- return this;
- }
-
- @Override
- public PackageImpl setSystemExt(boolean systemExt) {
- this.privateFlags = systemExt
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT;
- return this;
- }
-
- @Override
- public PackageImpl setIsStub(boolean isStub) {
- this.isStub = isStub;
- return this;
- }
-
- @Override
- public PackageImpl setCoreApp(boolean coreApp) {
- this.coreApp = coreApp;
- return this;
- }
-
- @Override
- public ParsedPackage capPermissionPriorities() {
- if (permissionGroups != null && !permissionGroups.isEmpty()) {
- for (int i = permissionGroups.size() - 1; i >= 0; --i) {
- // TODO(b/135203078): Builder/immutability
- permissionGroups.get(i).priority = 0;
- }
- }
- return this;
- }
-
- @Override
- public ParsedPackage clearProtectedBroadcasts() {
- if (protectedBroadcasts != null) {
- protectedBroadcasts.clear();
- }
- return this;
- }
-
- @Override
- public ParsedPackage markNotActivitiesAsNotExportedIfSingleUser() {
- // ignore export request for single user receivers
- if (receivers != null) {
- for (ParsedActivity receiver : receivers) {
- if ((receiver.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
- receiver.exported = false;
- }
- }
- }
- // ignore export request for single user services
- if (services != null) {
- for (ParsedService service : services) {
- if ((service.flags & ServiceInfo.FLAG_SINGLE_USER) != 0) {
- service.exported = false;
- }
- }
- }
- // ignore export request for single user providers
- if (providers != null) {
- for (ParsedProvider provider : providers) {
- if ((provider.flags & ProviderInfo.FLAG_SINGLE_USER) != 0) {
- provider.exported = false;
- }
- }
- }
-
- return this;
- }
-
- @Override
- public ParsedPackage setPrivileged(boolean privileged) {
- this.privateFlags = privileged
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
- return this;
- }
-
- @Override
- public ParsedPackage setOem(boolean oem) {
- this.privateFlags = oem
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_OEM
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_OEM;
- return this;
- }
-
- @Override
- public ParsedPackage setVendor(boolean vendor) {
- this.privateFlags = vendor
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_VENDOR
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_VENDOR;
- return this;
- }
-
- @Override
- public ParsedPackage setProduct(boolean product) {
- this.privateFlags = product
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_PRODUCT
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_PRODUCT;
- return this;
- }
-
- @Override
- public ParsedPackage setOdm(boolean odm) {
- this.privateFlags = odm
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_ODM
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_ODM;
- return this;
- }
-
- @Override
- public ParsedPackage setSignedWithPlatformKey(boolean signedWithPlatformKey) {
- this.privateFlags = signedWithPlatformKey
- ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
- : this.privateFlags & ~ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
- return this;
- }
-
- @Override
- public ParsedPackage clearOriginalPackages() {
- if (originalPackages != null) {
- originalPackages.clear();
- }
- return this;
- }
-
- @Override
- public ParsedPackage clearAdoptPermissions() {
- if (adoptPermissions != null) {
- adoptPermissions.clear();
- }
- return this;
- }
-
- @Override
- public PackageImpl addUsesLibrary(int index, String libraryName) {
- this.usesLibraries = ArrayUtils.add(usesLibraries, index, libraryName);
- return this;
- }
-
- @Override
- public ParsedPackage removeUsesLibrary(String libraryName) {
- this.usesLibraries = ArrayUtils.remove(this.usesLibraries, libraryName);
- return this;
- }
-
- @Override
- public PackageImpl addUsesOptionalLibrary(int index, String libraryName) {
- this.usesOptionalLibraries = ArrayUtils.add(usesOptionalLibraries, index, libraryName);
- return this;
- }
-
- @Nullable
- @Override
- public List<String> getUsesOptionalLibraries() {
- return usesOptionalLibraries;
- }
-
- @Override
- public int getVersionCode() {
- return versionCode;
- }
-
- @Nullable
- @Override
- public long[] getUsesStaticLibrariesVersions() {
- return usesStaticLibrariesVersions;
- }
-
- @Override
- public PackageImpl setPackageSettingCallback(PackageSettingCallback packageSettingCallback) {
- packageSettingCallback.setAndroidPackage(this);
- return this;
- }
-
- @Override
- public PackageImpl setUpdatedSystemApp(boolean updatedSystemApp) {
- this.flags = updatedSystemApp
- ? this.flags | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
- : this.flags & ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
- return this;
- }
-
- @Override
- public boolean isPrivileged() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
- }
-
- @Override
- public PackageImpl setSeInfo(String seInfo) {
- this.seInfo = seInfo;
- return this;
- }
-
- @Override
- public PackageImpl setSeInfoUser(String seInfoUser) {
- this.seInfoUser = seInfoUser;
- return this;
- }
-
- @Override
- public PackageImpl initForUser(int userId) {
- // TODO(b/135203078): Move this user state to some other data structure
- this.uid = UserHandle.getUid(userId, UserHandle.getAppId(this.uid));
-
- if ("android".equals(packageName)) {
- dataDir = Environment.getDataSystemDirectory().getAbsolutePath();
- return this;
- }
-
- deviceProtectedDataDir = Environment
- .getDataUserDePackageDirectory(applicationVolumeUuid, userId, packageName)
- .getAbsolutePath();
- credentialProtectedDataDir = Environment
- .getDataUserCePackageDirectory(applicationVolumeUuid, userId, packageName)
- .getAbsolutePath();
-
- if ((privateFlags & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) != 0
- && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
- dataDir = deviceProtectedDataDir;
- } else {
- dataDir = credentialProtectedDataDir;
- }
- return this;
- }
-
- @Override
- public ParsedPackage setFactoryTest(boolean factoryTest) {
- this.flags = factoryTest
- ? this.flags | ApplicationInfo.FLAG_FACTORY_TEST
- : this.flags & ~ApplicationInfo.FLAG_FACTORY_TEST;
- return this;
- }
-
- @Override
- public String getManifestPackageName() {
- return manifestPackageName;
- }
-
- @Override
- public String getRealPackage() {
- return realPackage;
- }
-
- @Override
- public String getOverlayTarget() {
- return overlayTarget;
- }
-
- @Override
- public String getOverlayTargetName() {
- return overlayTargetName;
- }
-
- @Override
- public Map<String, String> getOverlayables() {
- return overlayables;
- }
-
- @Override
- public boolean isOverlayIsStatic() {
- return overlayIsStatic;
- }
-
- @Override
- public int[] getSplitFlags() {
- return splitFlags;
- }
-
- @Deprecated
- @Override
- public String getApplicationInfoVolumeUuid() {
- return applicationVolumeUuid;
- }
-
- @Nullable
- @Override
- public List<String> getProtectedBroadcasts() {
- return protectedBroadcasts;
- }
-
- @Nullable
- @Override
- public Set<String> getUpgradeKeySets() {
- return upgradeKeySets;
- }
-
- @Nullable
- @Override
- public String[][] getUsesStaticLibrariesCertDigests() {
- return usesStaticLibrariesCertDigests;
- }
-
- @Override
- public int getOverlayPriority() {
- return overlayPriority;
- }
-
- @Deprecated
- @Override
- public String getAppInfoPackageName() {
- return packageName;
- }
-
- @Override
- public UUID getStorageUuid() {
- return StorageManager.convert(applicationVolumeUuid);
- }
-
- @Override
- public int getUid() {
- return uid;
- }
-
- @Override
- public boolean isStub() {
- return isStub;
- }
-
- @Deprecated
- @Override
- public String getAppInfoCodePath() {
- return applicationInfoCodePath;
- }
-
- @Override
- public boolean isSystem() {
- return (flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- }
-
- @Override
- public boolean isMatch(int flags) {
- if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- return isSystem();
- }
- return true;
- }
-
- @Override
- public boolean isVisibleToInstantApps() {
- return visibleToInstantApps;
- }
-
- @Override
- public PackageImpl setLastPackageUsageTimeInMills(int reason, long time) {
- lastPackageUsageTimeInMills[reason] = time;
- return this;
- }
-
- @Override
- public List<SharedLibraryInfo> getUsesLibraryInfos() {
- return usesLibraryInfos;
- }
-
- @NonNull
- @Override
- public List<String> getAllCodePaths() {
- return makeListAllCodePaths();
- }
-
- @Nullable
- @Override
- public String[] getUsesLibraryFiles() {
- return usesLibraryFiles;
- }
-
- @Override
- public PackageImpl setUsesLibraryInfos(
- @Nullable List<SharedLibraryInfo> usesLibraryInfos) {
- this.usesLibraryInfos = usesLibraryInfos;
- return this;
- }
-
- @Override
- public PackageImpl setUsesLibraryFiles(@Nullable String[] usesLibraryFiles) {
- this.usesLibraryFiles = usesLibraryFiles;
- return this;
- }
-
- @Override
- public PackageImpl setUid(int uid) {
- this.uid = uid;
- return this;
- }
-
- @Override
- public List<String> getAdoptPermissions() {
- return adoptPermissions;
- }
-
- @Override
- public ApplicationInfo toAppInfoWithoutState() {
- updateFlags();
-
- ApplicationInfo appInfo = new ApplicationInfo();
- appInfo.packageName = packageName;
- appInfo.flags = flags;
- appInfo.privateFlags = privateFlags;
-
- appInfo.appComponentFactory = appComponentFactory;
- appInfo.backupAgentName = backupAgentName;
- appInfo.banner = banner;
- appInfo.category = category;
- appInfo.classLoaderName = classLoaderName;
- appInfo.className = className;
- appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
- appInfo.compileSdkVersion = compileSdkVersion;
- appInfo.compileSdkVersionCodename = compileSdkVersionCodename;
- appInfo.credentialProtectedDataDir = credentialProtectedDataDir;
- appInfo.dataDir = dataDir;
- appInfo.descriptionRes = descriptionRes;
- appInfo.deviceProtectedDataDir = deviceProtectedDataDir;
- appInfo.enabled = enabled;
- appInfo.fullBackupContent = fullBackupContent;
- appInfo.hiddenUntilInstalled = hiddenUntilInstalled;
- appInfo.icon = icon;
- appInfo.iconRes = iconRes;
- appInfo.installLocation = installLocation;
- appInfo.labelRes = labelRes;
- appInfo.largestWidthLimitDp = largestWidthLimitDp;
- appInfo.logo = logo;
- appInfo.manageSpaceActivityName = manageSpaceActivityName;
- appInfo.maxAspectRatio = maxAspectRatio;
- appInfo.metaData = appMetaData;
- appInfo.minAspectRatio = minAspectRatio;
- appInfo.minSdkVersion = minSdkVersion;
- appInfo.name = className;
- if (appInfo.name != null) {
- appInfo.name = appInfo.name.trim();
- }
- appInfo.nativeLibraryDir = nativeLibraryDir;
- appInfo.nativeLibraryRootDir = nativeLibraryRootDir;
- appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
- appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
- appInfo.nonLocalizedLabel = nonLocalizedLabel;
- if (appInfo.nonLocalizedLabel != null) {
- appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim();
- }
- appInfo.packageName = packageName;
- appInfo.permission = permission;
- appInfo.primaryCpuAbi = primaryCpuAbi;
- appInfo.processName = getProcessName();
- appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
- appInfo.roundIconRes = roundIconRes;
- appInfo.secondaryCpuAbi = secondaryCpuAbi;
- appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
- appInfo.seInfo = seInfo;
- appInfo.seInfoUser = seInfoUser;
- appInfo.sharedLibraryFiles = usesLibraryFiles;
- appInfo.sharedLibraryInfos = ArrayUtils.isEmpty(usesLibraryInfos) ? null : usesLibraryInfos;
- appInfo.splitClassLoaderNames = splitClassLoaderNames;
- appInfo.splitDependencies = splitDependencies;
- appInfo.splitNames = splitNames;
- appInfo.storageUuid = StorageManager.convert(volumeUuid);
- appInfo.targetSandboxVersion = targetSandboxVersion;
- appInfo.targetSdkVersion = targetSdkVersion;
- appInfo.taskAffinity = taskAffinity;
- appInfo.theme = theme;
- appInfo.uid = uid;
- appInfo.uiOptions = uiOptions;
- appInfo.volumeUuid = volumeUuid;
- appInfo.zygotePreloadName = zygotePreloadName;
- appInfo.crossProfile = isCrossProfile();
-
- appInfo.setBaseCodePath(baseCodePath);
- appInfo.setBaseResourcePath(baseCodePath);
- appInfo.setCodePath(codePath);
- appInfo.setResourcePath(codePath);
- appInfo.setSplitCodePaths(splitCodePaths);
- appInfo.setSplitResourcePaths(splitCodePaths);
- appInfo.setVersionCode(getLongVersionCode());
-
- // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo.
-// appInfo.showUserIcon = pkg.getShowUserIcon();
- // TODO(b/135203078): Unused?
-// appInfo.resourceDirs = pkg.getResourceDirs();
- // TODO(b/135203078): Unused?
-// appInfo.enabledSetting = pkg.getEnabledSetting();
- // TODO(b/135203078): See PackageImpl#getHiddenApiEnforcementPolicy
-// appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy();
-
- return appInfo;
- }
-
- @Override
- public PackageImpl setVersionCode(int versionCode) {
- this.versionCode = versionCode;
- return this;
- }
-
- @Override
- public PackageImpl setHiddenUntilInstalled(boolean hidden) {
- this.hiddenUntilInstalled = hidden;
- return this;
- }
-
- @Override
- public String getSeInfo() {
- return seInfo;
- }
-
- @Deprecated
- @Override
- public String getAppInfoResourcePath() {
- return applicationInfoResourcePath;
- }
-
- @Override
- public boolean isForwardLocked() {
- // TODO(b/135203078): Unused? Move to debug flag?
- return false;
- }
-
- @Override
- public byte[] getRestrictUpdateHash() {
- return restrictUpdateHash;
- }
-
- @Override
- public boolean hasComponentClassName(String className) {
- if (activities != null) {
- for (ParsedActivity parsedActivity : activities) {
- if (Objects.equals(className, parsedActivity.className)) {
- return true;
- }
- }
- }
-
- if (receivers != null) {
- for (ParsedActivity receiver : receivers) {
- if (Objects.equals(className, receiver.className)) {
- return true;
- }
- }
- }
-
- if (providers != null) {
- for (ParsedProvider provider : providers) {
- if (Objects.equals(className, provider.className)) {
- return true;
- }
- }
- }
-
- if (services != null) {
- for (ParsedService service : services) {
- if (Objects.equals(className, service.className)) {
- return true;
- }
- }
- }
-
- if (instrumentations != null) {
- for (ParsedInstrumentation instrumentation : instrumentations) {
- if (Objects.equals(className, instrumentation.className)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- @Override
- public boolean isDefaultToDeviceProtectedStorage() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
- != 0;
- }
-
- @Override
- public boolean isInternal() {
- return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0;
- }
-
- @Override
- public int getBaseRevisionCode() {
- return baseRevisionCode;
- }
-
- @Override
- public int[] getSplitRevisionCodes() {
- return splitRevisionCodes;
- }
-
- @Override
- public boolean canHaveOatDir() {
- // The following app types CANNOT have oat directory
- // - non-updated system apps
- return !isSystem() || isUpdatedSystemApp();
- }
-
- @Override
- public long getLatestPackageUseTimeInMills() {
- long latestUse = 0L;
- for (long use : lastPackageUsageTimeInMills) {
- latestUse = Math.max(latestUse, use);
- }
- return latestUse;
- }
-
- @Override
- public long getLatestForegroundPackageUseTimeInMills() {
- int[] foregroundReasons = {
- PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY,
- PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE
- };
-
- long latestUse = 0L;
- for (int reason : foregroundReasons) {
- latestUse = Math.max(latestUse, lastPackageUsageTimeInMills[reason]);
- }
- return latestUse;
- }
-
- @Override
- public boolean isCoreApp() {
- return coreApp;
- }
-
- @Override
- public String getVersionName() {
- return versionName;
- }
-
- @Override
- public PackageImpl setVersionCodeMajor(int versionCodeMajor) {
- this.versionCodeMajor = versionCodeMajor;
- return this;
- }
-
- @Override
- public long[] getLastPackageUsageTimeInMills() {
- return lastPackageUsageTimeInMills;
- }
-
- @Override
- public String getDataDir() {
- return dataDir;
- }
-
- @Override
- public boolean isExternal() {
- return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- }
-
- @Override
- public List<String> getImplicitPermissions() {
- return implicitPermissions == null ? Collections.emptyList() : implicitPermissions;
- }
-
- /**
- * TODO(b/135203078): Remove, ensure b/140256621 is fixed or irrelevant
- * TODO(b/140256621): Remove after fixing instant app check
- * @deprecated This method always returns false because there's no paired set method
- */
- @Deprecated
- @Override
- public boolean isInstantApp() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
- }
-
- @Override
- public boolean hasRequestedLegacyExternalStorage() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0;
- }
-
- @Override
- public boolean isVendor() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
- }
-
- @Override
- public boolean isProduct() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
- }
-
- @Override
- public boolean isOem() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
- }
-
- @Override
- public boolean isEncryptionAware() {
- boolean isPartiallyDirectBootAware =
- (privateFlags & ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) != 0;
- return isDirectBootAware() || isPartiallyDirectBootAware;
- }
-
- @Override
- public boolean isEmbeddedDexUsed() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX) != 0;
- }
-
- @Deprecated
- @Override
- public String getAppInfoProcessName() {
- return processName;
- }
-
- @Override
- public List<String> getAllCodePathsExcludingResourceOnly() {
- ArrayList<String> paths = new ArrayList<>();
- if ((flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
- paths.add(baseCodePath);
- }
- if (!ArrayUtils.isEmpty(splitCodePaths)) {
- for (int i = 0; i < splitCodePaths.length; i++) {
- if ((splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
- paths.add(splitCodePaths[i]);
- }
- }
- }
- return paths;
- }
-
- @Deprecated
- @Override
- public String getAppInfoName() {
- return name;
- }
-
- private boolean isSignedWithPlatformKey() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
- }
-
- private boolean usesNonSdkApi() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API) != 0;
- }
-
- private boolean isPackageWhitelistedForHiddenApis() {
- return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
- }
-
- private boolean isAllowedToUseHiddenApis() {
- if (isSignedWithPlatformKey()) {
- return true;
- } else if (isSystemApp() || isUpdatedSystemApp()) {
- return usesNonSdkApi() || isPackageWhitelistedForHiddenApis();
- } else {
- return false;
- }
- }
-
- @Override
- public int getHiddenApiEnforcementPolicy() {
- if (isAllowedToUseHiddenApis()) {
- return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
- }
-
- // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done
- // entirely through ApplicationInfo and shouldn't touch this specific class, but that
- // may not always hold true.
-// if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) {
-// return mHiddenApiPolicy;
-// }
- return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED;
- }
-
- @Nullable
- @Override
- public SparseArray<int[]> getSplitDependencies() {
- return splitDependencies;
- }
-
- @Override
- public boolean requestsIsolatedSplitLoading() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) != 0;
- }
-
- @Deprecated
- @Override
- public String getAppInfoClassLoaderName() {
- return classLoaderName;
- }
-
- @Override
- public String getClassLoaderName() {
- return classLoaderName;
- }
-
- @Override
- public String[] getSplitClassLoaderNames() {
- return splitClassLoaderNames;
- }
-
- @Override
- public String getOverlayCategory() {
- return overlayCategory;
- }
-
- @Override
- public boolean isProfileableByShell() {
- return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) != 0;
- }
-
- @Nullable
- @Override
- public List<ParsedActivityIntentInfo> getPreferredActivityFilters() {
- return preferredActivityFilters;
- }
-
- @Override
- public boolean isHiddenUntilInstalled() {
- return hiddenUntilInstalled;
- }
-
- @Override
- public int getMinSdkVersion() {
- return minSdkVersion;
- }
-
- @Override
- public String getRestrictedAccountType() {
- return restrictedAccountType;
- }
-
- @Override
- public String getRequiredAccountType() {
- return requiredAccountType;
- }
-
- @Override
- public int getInstallLocation() {
- return installLocation;
- }
-
- @Override
- public List<ParsedActivity> getReceivers() {
- return receivers;
- }
-
- @Override
- public List<ParsedService> getServices() {
- return services;
- }
-
- @Override
- public List<ParsedProvider> getProviders() {
- return providers;
- }
-
- @Override
- public int getSharedUserLabel() {
- return sharedUserLabel;
- }
-
- @Override
- public int getVersionCodeMajor() {
- return versionCodeMajor;
- }
-
- @Override
- public boolean isRequiredForAllUsers() {
- return requiredForAllUsers;
- }
-
- @Override
- public int getCompileSdkVersion() {
- return compileSdkVersion;
- }
-
- @Override
- public String getCompileSdkVersionCodeName() {
- return compileSdkVersionCodename;
- }
-
- @Nullable
- @Override
- public List<ConfigurationInfo> getConfigPreferences() {
- return configPreferences;
- }
-
- @Nullable
- @Override
- public List<FeatureInfo> getReqFeatures() {
- return reqFeatures;
- }
-
- @Override
- public List<FeatureGroupInfo> getFeatureGroups() {
- return featureGroups;
- }
-
- @Override
- public String getDeviceProtectedDataDir() {
- return deviceProtectedDataDir;
- }
-
- @Override
- public String getCredentialProtectedDataDir() {
- return credentialProtectedDataDir;
- }
-
- @Override
- public String getSeInfoUser() {
- return seInfoUser;
- }
-
- @Override
- public String getClassName() {
- return className;
- }
-
- @Override
- public int getTheme() {
- return theme;
- }
-
- @Override
- public int getRequiresSmallestWidthDp() {
- return requiresSmallestWidthDp;
- }
-
- @Override
- public int getCompatibleWidthLimitDp() {
- return compatibleWidthLimitDp;
- }
-
- @Override
- public int getLargestWidthLimitDp() {
- return largestWidthLimitDp;
- }
-
- @Override
- public String getScanSourceDir() {
- return applicationInfoCodePath;
- }
-
- @Override
- public String getScanPublicSourceDir() {
- return applicationInfoResourcePath;
- }
-
- @Override
- public String getPublicSourceDir() {
- return applicationInfoBaseResourcePath;
- }
-
- @Override
- public String[] getSplitPublicSourceDirs() {
- return applicationInfoSplitResourcePaths;
- }
-
- @Override
- public String getSecondaryNativeLibraryDir() {
- return secondaryNativeLibraryDir;
- }
-
- @Override
- public boolean isEnabled() {
- return enabled;
- }
-
- @Override
- public boolean isCrossProfile() {
- return crossProfile;
- }
-
- @Override
- public String getManageSpaceActivityName() {
- return manageSpaceActivityName;
- }
-
- @Override
- public int getDescriptionRes() {
- return descriptionRes;
- }
-
- @Override
- public String getBackupAgentName() {
- return backupAgentName;
- }
-
- @Override
- public int getFullBackupContent() {
- return fullBackupContent;
- }
-
- @Override
- public int getNetworkSecurityConfigRes() {
- return networkSecurityConfigRes;
- }
-
- @Override
- public int getCategory() {
- return category;
- }
-
- @Override
- public int getTargetSandboxVersion() {
- return targetSandboxVersion;
- }
-
- @Override
- public String getAppComponentFactory() {
- return appComponentFactory;
- }
-
- @Override
- public int getIconRes() {
- return iconRes;
- }
-
- @Override
- public int getRoundIconRes() {
- return roundIconRes;
- }
-
- @Override
- public String getZygotePreloadName() {
- return zygotePreloadName;
- }
-
- @Override
- public int getLabelRes() {
- return labelRes;
- }
-
- @Override
- public CharSequence getNonLocalizedLabel() {
- return nonLocalizedLabel;
- }
-
- @Override
- public int getIcon() {
- return icon;
- }
-
- @Override
- public int getBanner() {
- return banner;
- }
-
- @Override
- public int getLogo() {
- return logo;
- }
-
- @Override
- public Bundle getMetaData() {
- return appMetaData;
- }
-
- private void addMimeGroupsFromComponent(ParsedComponent<?> component) {
- for (int i = component.intents.size() - 1; i >= 0; i--) {
- IntentFilter filter = component.intents.get(i);
- for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
- mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
- }
- }
- }
-
- @Override
- @Nullable
- public Set<String> getMimeGroups() {
- return mimeGroups;
- }
-
- @Override
- @Nullable
- public List<Intent> getQueriesIntents() {
- return queriesIntents;
- }
-
- @Override
- @Nullable
- public List<String> getQueriesPackages() {
- return queriesPackages;
- }
-
- @Override
- public Set<String> getQueriesProviders() {
- return queriesProviders;
- }
-
- @Override
- public boolean hasPreserveLegacyExternalStorage() {
- return preserveLegacyExternalStorage;
- }
-
- private static void internStringArrayList(List<String> list) {
- if (list != null) {
- final int N = list.size();
- for (int i = 0; i < N; ++i) {
- list.set(i, list.get(i).intern());
- }
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(this.supportsSmallScreens);
- dest.writeInt(this.supportsNormalScreens);
- dest.writeInt(this.supportsLargeScreens);
- dest.writeInt(this.supportsXLargeScreens);
- dest.writeInt(this.resizeable);
- dest.writeInt(this.anyDensity);
- dest.writeLongArray(this.lastPackageUsageTimeInMills);
- dest.writeInt(this.versionCode);
- dest.writeInt(this.versionCodeMajor);
- dest.writeInt(this.baseRevisionCode);
- dest.writeString(this.versionName);
- dest.writeBoolean(this.coreApp);
- dest.writeInt(this.compileSdkVersion);
- dest.writeString(this.compileSdkVersionCodename);
- dest.writeString(this.packageName);
- dest.writeString(this.realPackage);
- dest.writeString(this.manifestPackageName);
- dest.writeString(this.baseCodePath);
- dest.writeBoolean(this.requiredForAllUsers);
- dest.writeString(this.restrictedAccountType);
- dest.writeString(this.requiredAccountType);
- dest.writeBoolean(this.baseHardwareAccelerated);
- dest.writeString(this.overlayTarget);
- dest.writeString(this.overlayTargetName);
- dest.writeString(this.overlayCategory);
- dest.writeInt(this.overlayPriority);
- dest.writeBoolean(this.overlayIsStatic);
- dest.writeMap(this.overlayables);
- dest.writeString(this.staticSharedLibName);
- dest.writeLong(this.staticSharedLibVersion);
- dest.writeStringList(this.libraryNames);
- dest.writeStringList(this.usesLibraries);
- dest.writeStringList(this.usesOptionalLibraries);
- dest.writeStringList(this.usesStaticLibraries);
- dest.writeLongArray(this.usesStaticLibrariesVersions);
- final int numProcesses = this.processes != null ? this.processes.size() : 0;
- dest.writeInt(numProcesses);
- for (int i = 0; i < numProcesses; i++) {
- this.processes.valueAt(i).writeToParcel(dest, 0);
- }
-
- if (this.usesStaticLibrariesCertDigests == null) {
- dest.writeInt(-1);
- } else {
- dest.writeInt(this.usesStaticLibrariesCertDigests.length);
- for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) {
- dest.writeStringArray(this.usesStaticLibrariesCertDigests[index]);
- }
- }
-
- dest.writeString(this.sharedUserId);
- dest.writeInt(this.sharedUserLabel);
- dest.writeTypedList(this.configPreferences);
- dest.writeTypedList(this.reqFeatures);
- dest.writeTypedList(this.featureGroups);
- dest.writeByteArray(this.restrictUpdateHash);
- dest.writeStringList(this.originalPackages);
- dest.writeStringList(this.adoptPermissions);
- dest.writeStringList(this.requestedPermissions);
- dest.writeStringList(this.implicitPermissions);
- dest.writeArraySet(this.upgradeKeySets);
- dest.writeMap(this.keySetMapping);
- dest.writeStringList(this.protectedBroadcasts);
- dest.writeTypedList(this.activities);
- dest.writeTypedList(this.receivers);
- dest.writeTypedList(this.services);
- dest.writeTypedList(this.providers);
- dest.writeTypedList(this.features);
- dest.writeTypedList(this.permissions);
- dest.writeTypedList(this.permissionGroups);
- dest.writeTypedList(this.instrumentations);
- ParsedIntentInfo.writeIntentsList(this.preferredActivityFilters, dest, flags);
- dest.writeBundle(this.appMetaData);
- dest.writeString(this.volumeUuid);
- dest.writeString(this.applicationVolumeUuid);
- dest.writeParcelable(this.signingDetails, flags);
- dest.writeString(this.codePath);
- dest.writeBoolean(this.use32BitAbi);
- dest.writeBoolean(this.visibleToInstantApps);
- dest.writeString(this.cpuAbiOverride);
- dest.writeBoolean(this.isStub);
- dest.writeInt(this.preferredOrder);
- dest.writeBoolean(this.forceQueryable);
- dest.writeParcelableList(this.queriesIntents, flags);
- dest.writeStringList(this.queriesPackages);
- dest.writeString(this.applicationInfoBaseResourcePath);
- dest.writeString(this.applicationInfoCodePath);
- dest.writeString(this.applicationInfoResourcePath);
- dest.writeStringArray(this.applicationInfoSplitResourcePaths);
- dest.writeString(this.appComponentFactory);
- dest.writeString(this.backupAgentName);
- dest.writeInt(this.banner);
- dest.writeInt(this.category);
- dest.writeString(this.classLoaderName);
- dest.writeString(this.className);
- dest.writeInt(this.compatibleWidthLimitDp);
- dest.writeString(this.credentialProtectedDataDir);
- dest.writeString(this.dataDir);
- dest.writeInt(this.descriptionRes);
- dest.writeString(this.deviceProtectedDataDir);
- dest.writeBoolean(this.enabled);
- dest.writeBoolean(this.crossProfile);
- dest.writeInt(this.flags);
- dest.writeInt(this.fullBackupContent);
- dest.writeBoolean(this.hiddenUntilInstalled);
- dest.writeInt(this.icon);
- dest.writeInt(this.iconRes);
- dest.writeInt(this.installLocation);
- dest.writeInt(this.labelRes);
- dest.writeInt(this.largestWidthLimitDp);
- dest.writeInt(this.logo);
- dest.writeString(this.manageSpaceActivityName);
- dest.writeFloat(this.maxAspectRatio);
- dest.writeFloat(this.minAspectRatio);
- dest.writeInt(this.minSdkVersion);
- dest.writeString(this.name);
- dest.writeString(this.nativeLibraryDir);
- dest.writeString(this.nativeLibraryRootDir);
- dest.writeBoolean(this.nativeLibraryRootRequiresIsa);
- dest.writeInt(this.networkSecurityConfigRes);
- dest.writeCharSequence(this.nonLocalizedLabel);
- dest.writeString(this.permission);
- dest.writeString(this.primaryCpuAbi);
- dest.writeInt(this.privateFlags);
- dest.writeString(this.processName);
- dest.writeInt(this.requiresSmallestWidthDp);
- dest.writeInt(this.roundIconRes);
- dest.writeString(this.secondaryCpuAbi);
- dest.writeString(this.secondaryNativeLibraryDir);
- dest.writeString(this.seInfo);
- dest.writeString(this.seInfoUser);
- dest.writeInt(this.targetSandboxVersion);
- dest.writeInt(this.targetSdkVersion);
- dest.writeString(this.taskAffinity);
- dest.writeInt(this.theme);
- dest.writeInt(this.uid);
- dest.writeInt(this.uiOptions);
- dest.writeStringArray(this.usesLibraryFiles);
- dest.writeTypedList(this.usesLibraryInfos);
- dest.writeString(this.zygotePreloadName);
- dest.writeStringArray(this.splitClassLoaderNames);
- dest.writeStringArray(this.splitCodePaths);
- dest.writeSparseArray(this.splitDependencies);
- dest.writeIntArray(this.splitFlags);
- dest.writeStringArray(this.splitNames);
- dest.writeIntArray(this.splitRevisionCodes);
- dest.writeArraySet(this.mimeGroups);
- }
-
- public PackageImpl(Parcel in) {
- // We use the boot classloader for all classes that we load.
- final ClassLoader boot = Object.class.getClassLoader();
- this.supportsSmallScreens = in.readInt();
- this.supportsNormalScreens = in.readInt();
- this.supportsLargeScreens = in.readInt();
- this.supportsXLargeScreens = in.readInt();
- this.resizeable = in.readInt();
- this.anyDensity = in.readInt();
- this.lastPackageUsageTimeInMills = in.createLongArray();
- this.versionCode = in.readInt();
- this.versionCodeMajor = in.readInt();
- this.baseRevisionCode = in.readInt();
- this.versionName = TextUtils.safeIntern(in.readString());
- this.coreApp = in.readBoolean();
- this.compileSdkVersion = in.readInt();
- this.compileSdkVersionCodename = TextUtils.safeIntern(in.readString());
- this.packageName = TextUtils.safeIntern(in.readString());
- this.realPackage = in.readString();
- this.manifestPackageName = in.readString();
- this.baseCodePath = in.readString();
- this.requiredForAllUsers = in.readBoolean();
- this.restrictedAccountType = in.readString();
- this.requiredAccountType = in.readString();
- this.baseHardwareAccelerated = in.readBoolean();
- this.overlayTarget = in.readString();
- this.overlayTargetName = in.readString();
- this.overlayCategory = in.readString();
- this.overlayPriority = in.readInt();
- this.overlayIsStatic = in.readBoolean();
- this.overlayables = new HashMap<>();
- in.readMap(overlayables, boot);
- this.staticSharedLibName = TextUtils.safeIntern(in.readString());
- this.staticSharedLibVersion = in.readLong();
- this.libraryNames = in.createStringArrayList();
- internStringArrayList(this.libraryNames);
- this.usesLibraries = in.createStringArrayList();
- internStringArrayList(this.usesLibraries);
- this.usesOptionalLibraries = in.createStringArrayList();
- internStringArrayList(this.usesOptionalLibraries);
- this.usesStaticLibraries = in.createStringArrayList();
- internStringArrayList(usesStaticLibraries);
- this.usesStaticLibrariesVersions = in.createLongArray();
- final int numProcesses = in.readInt();
- if (numProcesses > 0) {
- this.processes = new ArrayMap<>(numProcesses);
- for (int i = 0; i < numProcesses; i++) {
- ComponentParseUtils.ParsedProcess proc = new ComponentParseUtils.ParsedProcess(in);
- this.processes.put(proc.name, proc);
- }
- } else {
- this.processes = null;
- }
-
- int digestsSize = in.readInt();
- if (digestsSize >= 0) {
- this.usesStaticLibrariesCertDigests = new String[digestsSize][];
- for (int index = 0; index < digestsSize; index++) {
- this.usesStaticLibrariesCertDigests[index] = in.readStringArray();
- }
- }
-
- this.sharedUserId = TextUtils.safeIntern(in.readString());
- this.sharedUserLabel = in.readInt();
- this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);
- this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);
- this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR);
- this.restrictUpdateHash = in.createByteArray();
- this.originalPackages = in.createStringArrayList();
- this.adoptPermissions = in.createStringArrayList();
- this.requestedPermissions = in.createStringArrayList();
- internStringArrayList(this.requestedPermissions);
- this.implicitPermissions = in.createStringArrayList();
- internStringArrayList(this.implicitPermissions);
- this.upgradeKeySets = (ArraySet<String>) in.readArraySet(boot);
- this.keySetMapping = in.readHashMap(boot);
- this.protectedBroadcasts = in.createStringArrayList();
- internStringArrayList(this.protectedBroadcasts);
- this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
- this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
- this.services = in.createTypedArrayList(ParsedService.CREATOR);
- this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
- this.features = in.createTypedArrayList(ParsedFeature.CREATOR);
- this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
- this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
- this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
- this.preferredActivityFilters = ParsedIntentInfo.createIntentsList(in);
- this.appMetaData = in.readBundle(boot);
- this.volumeUuid = in.readString();
- this.applicationVolumeUuid = in.readString();
- this.signingDetails = in.readParcelable(boot);
- this.codePath = in.readString();
- this.use32BitAbi = in.readBoolean();
- this.visibleToInstantApps = in.readBoolean();
- this.cpuAbiOverride = in.readString();
- this.isStub = in.readBoolean();
- this.preferredOrder = in.readInt();
- this.forceQueryable = in.readBoolean();
- this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
- this.queriesPackages = in.createStringArrayList();
- internStringArrayList(this.queriesPackages);
- this.applicationInfoBaseResourcePath = in.readString();
- this.applicationInfoCodePath = in.readString();
- this.applicationInfoResourcePath = in.readString();
- this.applicationInfoSplitResourcePaths = in.createStringArray();
- this.appComponentFactory = in.readString();
- this.backupAgentName = in.readString();
- this.banner = in.readInt();
- this.category = in.readInt();
- this.classLoaderName = in.readString();
- this.className = in.readString();
- this.compatibleWidthLimitDp = in.readInt();
- this.credentialProtectedDataDir = in.readString();
- this.dataDir = in.readString();
- this.descriptionRes = in.readInt();
- this.deviceProtectedDataDir = in.readString();
- this.enabled = in.readBoolean();
- this.crossProfile = in.readBoolean();
- this.flags = in.readInt();
- this.fullBackupContent = in.readInt();
- this.hiddenUntilInstalled = in.readBoolean();
- this.icon = in.readInt();
- this.iconRes = in.readInt();
- this.installLocation = in.readInt();
- this.labelRes = in.readInt();
- this.largestWidthLimitDp = in.readInt();
- this.logo = in.readInt();
- this.manageSpaceActivityName = in.readString();
- this.maxAspectRatio = in.readFloat();
- this.minAspectRatio = in.readFloat();
- this.minSdkVersion = in.readInt();
- this.name = in.readString();
- this.nativeLibraryDir = in.readString();
- this.nativeLibraryRootDir = in.readString();
- this.nativeLibraryRootRequiresIsa = in.readBoolean();
- this.networkSecurityConfigRes = in.readInt();
- this.nonLocalizedLabel = in.readCharSequence();
- this.permission = TextUtils.safeIntern(in.readString());
- this.primaryCpuAbi = in.readString();
- this.privateFlags = in.readInt();
- this.processName = in.readString();
- this.requiresSmallestWidthDp = in.readInt();
- this.roundIconRes = in.readInt();
- this.secondaryCpuAbi = in.readString();
- this.secondaryNativeLibraryDir = in.readString();
- this.seInfo = in.readString();
- this.seInfoUser = in.readString();
- this.targetSandboxVersion = in.readInt();
- this.targetSdkVersion = in.readInt();
- this.taskAffinity = in.readString();
- this.theme = in.readInt();
- this.uid = in.readInt();
- this.uiOptions = in.readInt();
- this.usesLibraryFiles = in.createStringArray();
- this.usesLibraryInfos = in.createTypedArrayList(SharedLibraryInfo.CREATOR);
- this.zygotePreloadName = in.readString();
- this.splitClassLoaderNames = in.createStringArray();
- this.splitCodePaths = in.createStringArray();
- this.splitDependencies = in.readSparseArray(boot);
- this.splitFlags = in.createIntArray();
- this.splitNames = in.createStringArray();
- this.splitRevisionCodes = in.createIntArray();
- this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
- }
-
- public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
- @Override
- public PackageImpl createFromParcel(Parcel source) {
- return new PackageImpl(source);
- }
-
- @Override
- public PackageImpl[] newArray(int size) {
- return new PackageImpl[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/parsing/PackageInfoUtils.java b/core/java/android/content/pm/parsing/PackageInfoUtils.java
deleted file mode 100644
index 72df189..0000000
--- a/core/java/android/content/pm/parsing/PackageInfoUtils.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (C) 2019 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.parsing;
-
-import android.annotation.Nullable;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ComponentInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.FallbackCategoryProvider;
-import android.content.pm.FeatureGroupInfo;
-import android.content.pm.FeatureInfo;
-import android.content.pm.InstrumentationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageUserState;
-import android.content.pm.PermissionGroupInfo;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ProcessInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.SELinuxUtil;
-import android.content.pm.ServiceInfo;
-import android.content.pm.Signature;
-import android.content.pm.SigningInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.util.Set;
-
-/** @hide */
-public class PackageInfoUtils {
-
- private static final String TAG = ApkParseUtils.TAG;
-
- /**
- * Returns true if the package is installed and not hidden, or if the caller
- * explicitly wanted all uninstalled and hidden packages as well.
- */
- private static boolean checkUseInstalledOrHidden(AndroidPackage pkg, PackageUserState state,
- @PackageManager.PackageInfoFlags int flags) {
- // Returns false if the package is hidden system app until installed.
- if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
- && !state.installed
- && pkg.isHiddenUntilInstalled()) {
- return false;
- }
-
- // If available for the target user, or trying to match uninstalled packages and it's
- // a system app.
- return state.isAvailable(flags)
- || (pkg.isSystemApp()
- && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
- || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
- }
-
- public static PackageInfo generate(AndroidPackage pkg, int[] gids,
- @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
- Set<String> grantedPermissions, PackageUserState state, int userId) {
- if (!checkUseInstalledOrHidden(pkg, state, flags) || !pkg.isMatch(flags)) {
- return null;
- }
- ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
-
- PackageInfo pi = new PackageInfo();
- pi.packageName = pkg.getPackageName();
- pi.splitNames = pkg.getSplitNames();
- pi.versionCode = pkg.getVersionCode();
- pi.versionCodeMajor = pkg.getVersionCodeMajor();
- pi.baseRevisionCode = pkg.getBaseRevisionCode();
- pi.splitRevisionCodes = pkg.getSplitRevisionCodes();
- pi.versionName = pkg.getVersionName();
- pi.sharedUserId = pkg.getSharedUserId();
- pi.sharedUserLabel = pkg.getSharedUserLabel();
- pi.applicationInfo = applicationInfo;
- pi.installLocation = pkg.getInstallLocation();
- pi.isStub = pkg.isStub();
- pi.coreApp = pkg.isCoreApp();
- if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
- || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
- pi.requiredForAllUsers = pkg.isRequiredForAllUsers();
- }
- pi.restrictedAccountType = pkg.getRestrictedAccountType();
- pi.requiredAccountType = pkg.getRequiredAccountType();
- pi.overlayTarget = pkg.getOverlayTarget();
- pi.targetOverlayableName = pkg.getOverlayTargetName();
- pi.overlayCategory = pkg.getOverlayCategory();
- pi.overlayPriority = pkg.getOverlayPriority();
- pi.mOverlayIsStatic = pkg.isOverlayIsStatic();
- pi.compileSdkVersion = pkg.getCompileSdkVersion();
- pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
- pi.firstInstallTime = firstInstallTime;
- pi.lastUpdateTime = lastUpdateTime;
- if ((flags & PackageManager.GET_GIDS) != 0) {
- pi.gids = gids;
- }
- if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
- int size = pkg.getConfigPreferences() != null ? pkg.getConfigPreferences().size() : 0;
- if (size > 0) {
- pi.configPreferences = new ConfigurationInfo[size];
- pkg.getConfigPreferences().toArray(pi.configPreferences);
- }
- size = pkg.getReqFeatures() != null ? pkg.getReqFeatures().size() : 0;
- if (size > 0) {
- pi.reqFeatures = new FeatureInfo[size];
- pkg.getReqFeatures().toArray(pi.reqFeatures);
- }
- size = pkg.getFeatureGroups() != null ? pkg.getFeatureGroups().size() : 0;
- if (size > 0) {
- pi.featureGroups = new FeatureGroupInfo[size];
- pkg.getFeatureGroups().toArray(pi.featureGroups);
- }
- }
- if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
- if (pkg.getActivities() != null) {
- final int N = pkg.getActivities().size();
- if (N > 0) {
- int num = 0;
- final ActivityInfo[] res = new ActivityInfo[N];
- for (int i = 0; i < N; i++) {
- final ParsedActivity a = pkg.getActivities().get(i);
- if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), a, flags)) {
- if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
- a.className)) {
- continue;
- }
- res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
- userId);
- }
- }
- pi.activities = ArrayUtils.trimToSize(res, num);
- }
- }
- }
- if ((flags & PackageManager.GET_RECEIVERS) != 0) {
- if (pkg.getReceivers() != null) {
- final int size = pkg.getReceivers().size();
- if (size > 0) {
- int num = 0;
- final ActivityInfo[] res = new ActivityInfo[size];
- for (int i = 0; i < size; i++) {
- final ParsedActivity a = pkg.getReceivers().get(i);
- if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), a, flags)) {
- res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
- userId);
- }
- }
- pi.receivers = ArrayUtils.trimToSize(res, num);
- }
- }
- }
- if ((flags & PackageManager.GET_SERVICES) != 0) {
- if (pkg.getServices() != null) {
- final int size = pkg.getServices().size();
- if (size > 0) {
- int num = 0;
- final ServiceInfo[] res = new ServiceInfo[size];
- for (int i = 0; i < size; i++) {
- final ComponentParseUtils.ParsedService s = pkg.getServices().get(i);
- if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), s, flags)) {
- res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo,
- userId);
- }
- }
- pi.services = ArrayUtils.trimToSize(res, num);
- }
- }
- }
- if ((flags & PackageManager.GET_PROVIDERS) != 0) {
- if (pkg.getProviders() != null) {
- final int size = pkg.getProviders().size();
- if (size > 0) {
- int num = 0;
- final ProviderInfo[] res = new ProviderInfo[size];
- for (int i = 0; i < size; i++) {
- final ComponentParseUtils.ParsedProvider pr = pkg.getProviders()
- .get(i);
- if (state.isMatch(pkg.isSystem(), pkg.isEnabled(), pr, flags)) {
- res[num++] = generateProviderInfo(pkg, pr, flags, state,
- applicationInfo, userId);
- }
- }
- pi.providers = ArrayUtils.trimToSize(res, num);
- }
- }
- }
- if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
- if (pkg.getInstrumentations() != null) {
- int N = pkg.getInstrumentations().size();
- if (N > 0) {
- pi.instrumentation = new InstrumentationInfo[N];
- for (int i = 0; i < N; i++) {
- pi.instrumentation[i] = generateInstrumentationInfo(
- pkg.getInstrumentations().get(i), pkg, flags);
- }
- }
- }
- }
- if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
- if (pkg.getPermissions() != null) {
- int N = ArrayUtils.size(pkg.getPermissions());
- if (N > 0) {
- pi.permissions = new PermissionInfo[N];
- for (int i = 0; i < N; i++) {
- pi.permissions[i] = generatePermissionInfo(
- pkg.getPermissions().get(i),
- flags
- );
- }
- }
- }
- if (pkg.getRequestedPermissions() != null) {
- int N = pkg.getRequestedPermissions().size();
- if (N > 0) {
- pi.requestedPermissions = new String[N];
- pi.requestedPermissionsFlags = new int[N];
- for (int i = 0; i < N; i++) {
- final String perm = pkg.getRequestedPermissions().get(i);
- pi.requestedPermissions[i] = perm;
- // The notion of required permissions is deprecated but for compatibility.
- pi.requestedPermissionsFlags[i] |=
- PackageInfo.REQUESTED_PERMISSION_REQUIRED;
- if (grantedPermissions != null && grantedPermissions.contains(perm)) {
- pi.requestedPermissionsFlags[i] |=
- PackageInfo.REQUESTED_PERMISSION_GRANTED;
- }
- }
- }
- }
- }
-
- PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
- // deprecated method of getting signing certificates
- if ((flags & PackageManager.GET_SIGNATURES) != 0) {
- if (signingDetails.hasPastSigningCertificates()) {
- // Package has included signing certificate rotation information. Return the oldest
- // cert so that programmatic checks keep working even if unaware of key rotation.
- pi.signatures = new Signature[1];
- pi.signatures[0] = signingDetails.pastSigningCertificates[0];
- } else if (signingDetails.hasSignatures()) {
- // otherwise keep old behavior
- int numberOfSigs = signingDetails.signatures.length;
- pi.signatures = new Signature[numberOfSigs];
- System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
- numberOfSigs);
- }
- }
-
- // replacement for GET_SIGNATURES
- if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
- if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
- // only return a valid SigningInfo if there is signing information to report
- pi.signingInfo = new SigningInfo(signingDetails);
- } else {
- pi.signingInfo = null;
- }
- }
-
- return pi;
- }
-
- @Nullable
- public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
- @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
-
- if (pkg == null) return null;
- if (!checkUseInstalledOrHidden(pkg, state, flags) || !pkg.isMatch(flags)) {
- return null;
- }
-
- // Make shallow copy so we can store the metadata/libraries safely
- ApplicationInfo ai = pkg.toAppInfoWithoutState();
- ai.initForUser(userId);
- if ((flags & PackageManager.GET_META_DATA) == 0) {
- ai.metaData = null;
- }
- if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) {
- ai.sharedLibraryFiles = null;
- ai.sharedLibraryInfos = null;
- }
- if (state.stopped) {
- ai.flags |= ApplicationInfo.FLAG_STOPPED;
- } else {
- ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
- }
- updateApplicationInfo(ai, flags, state);
-
- return ai;
- }
-
- private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
- @Nullable ApplicationInfo applicationInfo, int userId) {
- if (a == null) return null;
- if (!checkUseInstalledOrHidden(pkg, state, flags)) {
- return null;
- }
- if (applicationInfo == null) {
- applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
- }
- // Make shallow copies so we can store the metadata safely
- ActivityInfo ai = new ActivityInfo();
- assignSharedFieldsForComponentInfo(ai, a);
- ai.targetActivity = a.targetActivity;
- ai.processName = a.getProcessName();
- ai.exported = a.exported;
- ai.theme = a.theme;
- ai.uiOptions = a.uiOptions;
- ai.parentActivityName = a.parentActivityName;
- ai.permission = a.getPermission();
- ai.taskAffinity = a.taskAffinity;
- ai.flags = a.flags;
- ai.privateFlags = a.privateFlags;
- ai.launchMode = a.launchMode;
- ai.documentLaunchMode = a.documentLaunchMode;
- ai.maxRecents = a.maxRecents;
- ai.configChanges = a.configChanges;
- ai.softInputMode = a.softInputMode;
- ai.persistableMode = a.persistableMode;
- ai.lockTaskLaunchMode = a.lockTaskLaunchMode;
- ai.screenOrientation = a.screenOrientation;
- ai.resizeMode = a.resizeMode;
- ai.maxAspectRatio = a.maxAspectRatio;
- ai.minAspectRatio = a.minAspectRatio;
- ai.requestedVrComponent = a.requestedVrComponent;
- ai.rotationAnimation = a.rotationAnimation;
- ai.colorMode = a.colorMode;
- ai.preferMinimalPostProcessing = a.preferMinimalPostProcessing;
- ai.windowLayout = a.windowLayout;
- ai.metaData = a.metaData;
- ai.applicationInfo = applicationInfo;
- return ai;
- }
-
- public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
- @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
- return generateActivityInfo(pkg, a, flags, state, null, userId);
- }
-
- private static ServiceInfo generateServiceInfo(AndroidPackage pkg,
- ComponentParseUtils.ParsedService s, @PackageManager.ComponentInfoFlags int flags,
- PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId) {
- if (s == null) return null;
- if (!checkUseInstalledOrHidden(pkg, state, flags)) {
- return null;
- }
- if (applicationInfo == null) {
- applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
- }
- // Make shallow copies so we can store the metadata safely
- ServiceInfo si = new ServiceInfo();
- assignSharedFieldsForComponentInfo(si, s);
- si.exported = s.exported;
- si.flags = s.flags;
- si.metaData = s.metaData;
- si.permission = s.getPermission();
- si.processName = s.getProcessName();
- si.mForegroundServiceType = s.foregroundServiceType;
- si.metaData = s.metaData;
- si.applicationInfo = applicationInfo;
- return si;
- }
-
- public static ServiceInfo generateServiceInfo(AndroidPackage pkg,
- ComponentParseUtils.ParsedService s, @PackageManager.ComponentInfoFlags int flags,
- PackageUserState state, int userId) {
- return generateServiceInfo(pkg, s, flags, state, null, userId);
- }
-
- private static ProviderInfo generateProviderInfo(AndroidPackage pkg,
- ComponentParseUtils.ParsedProvider p, @PackageManager.ComponentInfoFlags int flags,
- PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId) {
- if (p == null) return null;
- if (!checkUseInstalledOrHidden(pkg, state, flags)) {
- return null;
- }
- if (applicationInfo == null) {
- applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
- }
- // Make shallow copies so we can store the metadata safely
- ProviderInfo pi = new ProviderInfo();
- assignSharedFieldsForComponentInfo(pi, p);
- pi.exported = p.exported;
- pi.flags = p.flags;
- pi.processName = p.getProcessName();
- pi.authority = p.getAuthority();
- pi.isSyncable = p.isSyncable;
- pi.readPermission = p.getReadPermission();
- pi.writePermission = p.getWritePermission();
- pi.grantUriPermissions = p.grantUriPermissions;
- pi.forceUriPermissions = p.forceUriPermissions;
- pi.multiprocess = p.multiProcess;
- pi.initOrder = p.initOrder;
- pi.uriPermissionPatterns = p.uriPermissionPatterns;
- pi.pathPermissions = p.pathPermissions;
- pi.metaData = p.metaData;
- if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
- pi.uriPermissionPatterns = null;
- }
- pi.applicationInfo = applicationInfo;
- return pi;
- }
-
- public static ProviderInfo generateProviderInfo(AndroidPackage pkg,
- ComponentParseUtils.ParsedProvider p, @PackageManager.ComponentInfoFlags int flags,
- PackageUserState state, int userId) {
- return generateProviderInfo(pkg, p, flags, state, null, userId);
- }
-
- public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
- AndroidPackage pkg, @PackageManager.ComponentInfoFlags int flags) {
- if (i == null) return null;
-
- InstrumentationInfo ii = new InstrumentationInfo();
- assignSharedFieldsForPackageItemInfo(ii, i);
- ii.targetPackage = i.getTargetPackage();
- ii.targetProcesses = i.getTargetProcesses();
- ii.handleProfiling = i.handleProfiling;
- ii.functionalTest = i.functionalTest;
-
- ii.sourceDir = pkg.getBaseCodePath();
- ii.publicSourceDir = pkg.getBaseCodePath();
- ii.splitNames = pkg.getSplitNames();
- ii.splitSourceDirs = pkg.getSplitCodePaths();
- ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
- ii.splitDependencies = pkg.getSplitDependencies();
- ii.dataDir = pkg.getDataDir();
- ii.deviceProtectedDataDir = pkg.getDeviceProtectedDataDir();
- ii.credentialProtectedDataDir = pkg.getCredentialProtectedDataDir();
- ii.primaryCpuAbi = pkg.getPrimaryCpuAbi();
- ii.secondaryCpuAbi = pkg.getSecondaryCpuAbi();
- ii.nativeLibraryDir = pkg.getNativeLibraryDir();
- ii.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
-
- if ((flags & PackageManager.GET_META_DATA) == 0) {
- return ii;
- }
- ii.metaData = i.metaData;
- return ii;
- }
-
- public static ArrayMap<String, ProcessInfo> generateProcessInfo(
- ArrayMap<String, ComponentParseUtils.ParsedProcess> procs,
- @PackageManager.ComponentInfoFlags int flags) {
- if (procs == null) {
- return null;
- }
-
- final int numProcs = procs.size();
- ArrayMap<String, ProcessInfo> retProcs = new ArrayMap(numProcs);
- for (int i = 0; i < numProcs; i++) {
- ComponentParseUtils.ParsedProcess proc = procs.valueAt(i);
- retProcs.put(proc.name, new ProcessInfo(proc.name,
- proc.deniedPermissions != null
- ? new ArraySet<>(proc.deniedPermissions) : null));
- }
- return retProcs;
- }
-
- public static PermissionInfo generatePermissionInfo(ParsedPermission p,
- @PackageManager.ComponentInfoFlags int flags) {
- if (p == null) return null;
-
- PermissionInfo pi = new PermissionInfo(p.backgroundPermission);
- assignSharedFieldsForPackageItemInfo(pi, p);
- pi.group = p.getGroup();
- pi.requestRes = p.requestRes;
- pi.protectionLevel = p.protectionLevel;
- pi.descriptionRes = p.descriptionRes;
- pi.flags = p.flags;
-
- if ((flags & PackageManager.GET_META_DATA) == 0) {
- return pi;
- }
- pi.metaData = p.metaData;
- return pi;
- }
-
- public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
- @PackageManager.ComponentInfoFlags int flags) {
- if (pg == null) return null;
-
- PermissionGroupInfo pgi = new PermissionGroupInfo(
- pg.requestDetailResourceId,
- pg.backgroundRequestResourceId,
- pg.backgroundRequestDetailResourceId
- );
- assignSharedFieldsForPackageItemInfo(pgi, pg);
- pgi.descriptionRes = pg.descriptionRes;
- pgi.priority = pg.priority;
- pgi.requestRes = pg.requestRes;
- pgi.flags = pg.flags;
-
- if ((flags & PackageManager.GET_META_DATA) == 0) {
- return pgi;
- }
- pgi.metaData = pg.metaData;
- return pgi;
- }
-
- private static void updateApplicationInfo(ApplicationInfo ai,
- @PackageManager.ApplicationInfoFlags int flags,
- PackageUserState state) {
- // CompatibilityMode is global state.
- if (!PackageParser.sCompatibilityModeEnabled) {
- ai.disableCompatibilityMode();
- }
- if (state.installed) {
- ai.flags |= ApplicationInfo.FLAG_INSTALLED;
- } else {
- ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
- }
- if (state.suspended) {
- ai.flags |= ApplicationInfo.FLAG_SUSPENDED;
- } else {
- ai.flags &= ~ApplicationInfo.FLAG_SUSPENDED;
- }
- if (state.instantApp) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_INSTANT;
- } else {
- ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT;
- }
- if (state.virtualPreload) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
- } else {
- ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
- }
- if (state.hidden) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
- } else {
- ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN;
- }
- if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- ai.enabled = true;
- } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
- ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
- } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
- ai.enabled = false;
- }
- ai.enabledSetting = state.enabled;
- if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
- ai.category = state.categoryHint;
- }
- if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
- ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
- }
- ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
- ai.resourceDirs = state.getAllOverlayPaths();
- ai.icon = (PackageParser.sUseRoundIcon && ai.roundIconRes != 0)
- ? ai.roundIconRes : ai.iconRes;
- }
-
- private static void assignSharedFieldsForPackageItemInfo(PackageItemInfo packageItemInfo,
- ComponentParseUtils.ParsedComponent parsedComponent) {
- packageItemInfo.banner = parsedComponent.banner;
- packageItemInfo.icon = parsedComponent.icon;
- packageItemInfo.labelRes = parsedComponent.labelRes;
- packageItemInfo.logo = parsedComponent.logo;
- packageItemInfo.name = parsedComponent.className;
- packageItemInfo.nonLocalizedLabel = parsedComponent.nonLocalizedLabel;
- packageItemInfo.packageName = parsedComponent.getPackageName();
- }
-
- private static void assignSharedFieldsForComponentInfo(ComponentInfo componentInfo,
- ComponentParseUtils.ParsedComponent parsedComponent) {
- assignSharedFieldsForPackageItemInfo(componentInfo, parsedComponent);
- componentInfo.descriptionRes = parsedComponent.descriptionRes;
- componentInfo.directBootAware = parsedComponent.directBootAware;
- componentInfo.enabled = parsedComponent.enabled;
- componentInfo.splitName = parsedComponent.getSplitName();
- }
-
-}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
new file mode 100644
index 0000000..e7d91c2
--- /dev/null
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2020 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.parsing;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apex.ApexInfo;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FallbackCategoryProvider;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.SELinuxUtil;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.os.Environment;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
+import android.content.pm.parsing.component.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+
+import libcore.util.EmptyArray;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+/** @hide **/
+public class PackageInfoWithoutStateUtils {
+
+ @Nullable
+ public static PackageInfo generate(ParsingPackageRead pkg, int[] gids,
+ @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ Set<String> grantedPermissions, PackageUserState state, int userId) {
+ return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions,
+ state, userId, null);
+ }
+
+ @Nullable
+ public static PackageInfo generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags) {
+ return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
+ new PackageUserState(), UserHandle.getCallingUserId(), apexInfo);
+ }
+
+ @Nullable
+ private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids,
+ @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ Set<String> grantedPermissions, PackageUserState state, int userId,
+ @Nullable ApexInfo apexInfo) {
+ ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+ if (applicationInfo == null) {
+ return null;
+ }
+ PackageInfo info = generateWithoutComponents(pkg, gids, flags, firstInstallTime,
+ lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo);
+
+ if (info == null) {
+ return null;
+ }
+
+ if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+ final int N = pkg.getActivities().size();
+ if (N > 0) {
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[N];
+ for (int i = 0; i < N; i++) {
+ final ParsedActivity a = pkg.getActivities().get(i);
+ if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
+ flags)) {
+ if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+ a.getName())) {
+ continue;
+ }
+ res[num++] = generateActivityInfo(pkg, a, flags, state,
+ applicationInfo, userId);
+ }
+ }
+ info.activities = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+ final int size = pkg.getReceivers().size();
+ if (size > 0) {
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedActivity a = pkg.getReceivers().get(i);
+ if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a,
+ flags)) {
+ res[num++] = generateActivityInfo(pkg, a, flags, state,
+ applicationInfo, userId);
+ }
+ }
+ info.receivers = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_SERVICES) != 0) {
+ final int size = pkg.getServices().size();
+ if (size > 0) {
+ int num = 0;
+ final ServiceInfo[] res = new ServiceInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedService s = pkg.getServices().get(i);
+ if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), s,
+ flags)) {
+ res[num++] = generateServiceInfo(pkg, s, flags, state,
+ applicationInfo, userId);
+ }
+ }
+ info.services = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+ final int size = pkg.getProviders().size();
+ if (size > 0) {
+ int num = 0;
+ final ProviderInfo[] res = new ProviderInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedProvider pr = pkg.getProviders()
+ .get(i);
+ if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), pr,
+ flags)) {
+ res[num++] = generateProviderInfo(pkg, pr, flags, state,
+ applicationInfo, userId);
+ }
+ }
+ info.providers = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+ int N = pkg.getInstrumentations().size();
+ if (N > 0) {
+ info.instrumentation = new InstrumentationInfo[N];
+ for (int i = 0; i < N; i++) {
+ info.instrumentation[i] = generateInstrumentationInfo(
+ pkg.getInstrumentations().get(i), pkg, flags, userId);
+ }
+ }
+ }
+
+ return info;
+ }
+
+ @Nullable
+ public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids,
+ @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ Set<String> grantedPermissions, PackageUserState state, int userId,
+ @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) {
+ if (!checkUseInstalled(pkg, state, flags)) {
+ return null;
+ }
+
+ PackageInfo pi = new PackageInfo();
+ pi.packageName = pkg.getPackageName();
+ pi.splitNames = pkg.getSplitNames();
+ pi.versionCode = pkg.getVersionCode();
+ pi.versionCodeMajor = pkg.getVersionCodeMajor();
+ pi.baseRevisionCode = pkg.getBaseRevisionCode();
+ pi.splitRevisionCodes = pkg.getSplitRevisionCodes();
+ pi.versionName = pkg.getVersionName();
+ pi.sharedUserId = pkg.getSharedUserId();
+ pi.sharedUserLabel = pkg.getSharedUserLabel();
+ pi.applicationInfo = applicationInfo;
+ pi.installLocation = pkg.getInstallLocation();
+ if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+ pi.requiredForAllUsers = pkg.isRequiredForAllUsers();
+ }
+ pi.restrictedAccountType = pkg.getRestrictedAccountType();
+ pi.requiredAccountType = pkg.getRequiredAccountType();
+ pi.overlayTarget = pkg.getOverlayTarget();
+ pi.targetOverlayableName = pkg.getOverlayTargetName();
+ pi.overlayCategory = pkg.getOverlayCategory();
+ pi.overlayPriority = pkg.getOverlayPriority();
+ pi.mOverlayIsStatic = pkg.isOverlayIsStatic();
+ pi.compileSdkVersion = pkg.getCompileSdkVersion();
+ pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName();
+ pi.firstInstallTime = firstInstallTime;
+ pi.lastUpdateTime = lastUpdateTime;
+ if ((flags & PackageManager.GET_GIDS) != 0) {
+ pi.gids = gids;
+ }
+ if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) {
+ int size = pkg.getConfigPreferences().size();
+ if (size > 0) {
+ pi.configPreferences = new ConfigurationInfo[size];
+ pkg.getConfigPreferences().toArray(pi.configPreferences);
+ }
+ size = pkg.getReqFeatures().size();
+ if (size > 0) {
+ pi.reqFeatures = new FeatureInfo[size];
+ pkg.getReqFeatures().toArray(pi.reqFeatures);
+ }
+ size = pkg.getFeatureGroups().size();
+ if (size > 0) {
+ pi.featureGroups = new FeatureGroupInfo[size];
+ pkg.getFeatureGroups().toArray(pi.featureGroups);
+ }
+ }
+ if ((flags & PackageManager.GET_PERMISSIONS) != 0) {
+ int size = ArrayUtils.size(pkg.getPermissions());
+ if (size > 0) {
+ pi.permissions = new PermissionInfo[size];
+ for (int i = 0; i < size; i++) {
+ pi.permissions[i] = generatePermissionInfo(pkg.getPermissions().get(i),
+ flags);
+ }
+ }
+ size = pkg.getRequestedPermissions().size();
+ if (size > 0) {
+ pi.requestedPermissions = new String[size];
+ pi.requestedPermissionsFlags = new int[size];
+ for (int i = 0; i < size; i++) {
+ final String perm = pkg.getRequestedPermissions().get(i);
+ pi.requestedPermissions[i] = perm;
+ // The notion of required permissions is deprecated but for compatibility.
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+ if (grantedPermissions != null && grantedPermissions.contains(perm)) {
+ pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+ }
+ }
+ }
+ }
+
+ if (apexInfo != null) {
+ File apexFile = new File(apexInfo.modulePath);
+
+ pi.applicationInfo.sourceDir = apexFile.getPath();
+ pi.applicationInfo.publicSourceDir = apexFile.getPath();
+ if (apexInfo.isFactory) {
+ pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ } else {
+ pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+ }
+ if (apexInfo.isActive) {
+ pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ } else {
+ pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
+ pi.isApex = true;
+ }
+
+ PackageParser.SigningDetails signingDetails = pkg.getSigningDetails();
+ // deprecated method of getting signing certificates
+ if ((flags & PackageManager.GET_SIGNATURES) != 0) {
+ if (signingDetails.hasPastSigningCertificates()) {
+ // Package has included signing certificate rotation information. Return the oldest
+ // cert so that programmatic checks keep working even if unaware of key rotation.
+ pi.signatures = new Signature[1];
+ pi.signatures[0] = signingDetails.pastSigningCertificates[0];
+ } else if (signingDetails.hasSignatures()) {
+ // otherwise keep old behavior
+ int numberOfSigs = signingDetails.signatures.length;
+ pi.signatures = new Signature[numberOfSigs];
+ System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0,
+ numberOfSigs);
+ }
+ }
+
+ // replacement for GET_SIGNATURES
+ if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+ if (signingDetails != PackageParser.SigningDetails.UNKNOWN) {
+ // only return a valid SigningInfo if there is signing information to report
+ pi.signingInfo = new SigningInfo(signingDetails);
+ } else {
+ pi.signingInfo = null;
+ }
+ }
+
+ return pi;
+ }
+
+ @Nullable
+ public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg,
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) {
+ if (pkg == null) {
+ return null;
+ }
+
+ if (!checkUseInstalled(pkg, state, flags)) {
+ return null;
+ }
+
+ // Make shallow copy so we can store the metadata/libraries safely
+ ApplicationInfo ai = pkg.toAppInfoWithoutState();
+ // Init handles data directories
+ // TODO(b/135203078): Consolidate the data directory logic, remove initForUser
+ ai.initForUser(userId);
+
+ ai.flags = appInfoFlags(pkg);
+ ai.privateFlags = appInfoPrivateFlags(pkg);
+
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ ai.metaData = null;
+ }
+ if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) {
+ ai.sharedLibraryFiles = null;
+ ai.sharedLibraryInfos = null;
+ }
+
+ // CompatibilityMode is global state.
+ if (!PackageParser.sCompatibilityModeEnabled) {
+ ai.disableCompatibilityMode();
+ }
+
+ ai.flags |= flag(state.stopped, ApplicationInfo.FLAG_STOPPED)
+ | flag(state.installed, ApplicationInfo.FLAG_INSTALLED)
+ | flag(state.suspended, ApplicationInfo.FLAG_SUSPENDED);
+ ai.privateFlags |= flag(state.instantApp, ApplicationInfo.PRIVATE_FLAG_INSTANT)
+ | flag(state.virtualPreload, ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
+ | flag(state.hidden, ApplicationInfo.PRIVATE_FLAG_HIDDEN);
+
+ if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ ai.enabled = true;
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0;
+ } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ ai.enabled = false;
+ }
+ ai.enabledSetting = state.enabled;
+ if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+ ai.category = state.categoryHint;
+ }
+ if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) {
+ ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName);
+ }
+ ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state);
+ ai.resourceDirs = state.getOverlayPaths();
+
+ return ai;
+ }
+
+ @Nullable
+ public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @Nullable ApplicationInfo applicationInfo, int userId) {
+ if (a == null) return null;
+ if (!checkUseInstalled(pkg, state, flags)) {
+ return null;
+ }
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+ }
+ // Make shallow copies so we can store the metadata safely
+ ActivityInfo ai = new ActivityInfo();
+ assignSharedFieldsForComponentInfo(ai, a);
+ ai.targetActivity = a.getTargetActivity();
+ ai.processName = a.getProcessName();
+ ai.exported = a.isExported();
+ ai.theme = a.getTheme();
+ ai.uiOptions = a.getUiOptions();
+ ai.parentActivityName = a.getParentActivityName();
+ ai.permission = a.getPermission();
+ ai.taskAffinity = a.getTaskAffinity();
+ ai.flags = a.getFlags();
+ ai.privateFlags = a.getPrivateFlags();
+ ai.launchMode = a.getLaunchMode();
+ ai.documentLaunchMode = a.getDocumentLaunchMode();
+ ai.maxRecents = a.getMaxRecents();
+ ai.configChanges = a.getConfigChanges();
+ ai.softInputMode = a.getSoftInputMode();
+ ai.persistableMode = a.getPersistableMode();
+ ai.lockTaskLaunchMode = a.getLockTaskLaunchMode();
+ ai.screenOrientation = a.getScreenOrientation();
+ ai.resizeMode = a.getResizeMode();
+ Float maxAspectRatio = a.getMaxAspectRatio();
+ ai.maxAspectRatio = maxAspectRatio != null ? maxAspectRatio : 0f;
+ Float minAspectRatio = a.getMinAspectRatio();
+ ai.minAspectRatio = minAspectRatio != null ? minAspectRatio : 0f;
+ ai.requestedVrComponent = a.getRequestedVrComponent();
+ ai.rotationAnimation = a.getRotationAnimation();
+ ai.colorMode = a.getColorMode();
+ ai.preferMinimalPostProcessing = a.isPreferMinimalPostProcessing();
+ ai.windowLayout = a.getWindowLayout();
+ ai.metaData = a.getMetaData();
+ ai.applicationInfo = applicationInfo;
+ return ai;
+ }
+
+ @Nullable
+ public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+ return generateActivityInfo(pkg, a, flags, state, null, userId);
+ }
+
+ @Nullable
+ public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @Nullable ApplicationInfo applicationInfo, int userId) {
+ if (s == null) return null;
+ if (!checkUseInstalled(pkg, state, flags)) {
+ return null;
+ }
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+ }
+ // Make shallow copies so we can store the metadata safely
+ ServiceInfo si = new ServiceInfo();
+ assignSharedFieldsForComponentInfo(si, s);
+ si.exported = s.isExported();
+ si.flags = s.getFlags();
+ si.metaData = s.getMetaData();
+ si.permission = s.getPermission();
+ si.processName = s.getProcessName();
+ si.mForegroundServiceType = s.getForegroundServiceType();
+ si.applicationInfo = applicationInfo;
+ return si;
+ }
+
+ @Nullable
+ public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+ return generateServiceInfo(pkg, s, flags, state, null, userId);
+ }
+
+ @Nullable
+ public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @Nullable ApplicationInfo applicationInfo, int userId) {
+ if (p == null) return null;
+ if (!checkUseInstalled(pkg, state, flags)) {
+ return null;
+ }
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId);
+ }
+ // Make shallow copies so we can store the metadata safely
+ ProviderInfo pi = new ProviderInfo();
+ assignSharedFieldsForComponentInfo(pi, p);
+ pi.exported = p.isExported();
+ pi.flags = p.getFlags();
+ pi.processName = p.getProcessName();
+ pi.authority = p.getAuthority();
+ pi.isSyncable = p.isSyncable();
+ pi.readPermission = p.getReadPermission();
+ pi.writePermission = p.getWritePermission();
+ pi.grantUriPermissions = p.isGrantUriPermissions();
+ pi.forceUriPermissions = p.isForceUriPermissions();
+ pi.multiprocess = p.isMultiProcess();
+ pi.initOrder = p.getInitOrder();
+ pi.uriPermissionPatterns = p.getUriPermissionPatterns();
+ pi.pathPermissions = p.getPathPermissions();
+ pi.metaData = p.getMetaData();
+ if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) {
+ pi.uriPermissionPatterns = null;
+ }
+ pi.applicationInfo = applicationInfo;
+ return pi;
+ }
+
+ @Nullable
+ public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) {
+ return generateProviderInfo(pkg, p, flags, state, null, userId);
+ }
+
+ @Nullable
+ public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+ ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId) {
+ if (i == null) return null;
+
+ InstrumentationInfo ii = new InstrumentationInfo();
+ assignSharedFieldsForPackageItemInfo(ii, i);
+ ii.targetPackage = i.getTargetPackage();
+ ii.targetProcesses = i.getTargetProcesses();
+ ii.handleProfiling = i.isHandleProfiling();
+ ii.functionalTest = i.isFunctionalTest();
+
+ ii.sourceDir = pkg.getBaseCodePath();
+ ii.publicSourceDir = pkg.getBaseCodePath();
+ ii.splitNames = pkg.getSplitNames();
+ ii.splitSourceDirs = pkg.getSplitCodePaths();
+ ii.splitPublicSourceDirs = pkg.getSplitCodePaths();
+ ii.splitDependencies = pkg.getSplitDependencies();
+ ii.dataDir = getDataDir(pkg, userId).getAbsolutePath();
+ ii.deviceProtectedDataDir = getDeviceProtectedDataDir(pkg, userId).getAbsolutePath();
+ ii.credentialProtectedDataDir = getCredentialProtectedDataDir(pkg,
+ userId).getAbsolutePath();
+
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ return ii;
+ }
+ ii.metaData = i.getMetaData();
+ return ii;
+ }
+
+ @Nullable
+ public static PermissionInfo generatePermissionInfo(ParsedPermission p,
+ @PackageManager.ComponentInfoFlags int flags) {
+ if (p == null) return null;
+
+ PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission());
+
+ assignSharedFieldsForPackageItemInfo(pi, p);
+
+ pi.group = p.getGroup();
+ pi.requestRes = p.getRequestRes();
+ pi.protectionLevel = p.getProtectionLevel();
+ pi.descriptionRes = p.getDescriptionRes();
+ pi.flags = p.getFlags();
+
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ return pi;
+ }
+ pi.metaData = p.getMetaData();
+ return pi;
+ }
+
+ @Nullable
+ public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
+ @PackageManager.ComponentInfoFlags int flags) {
+ if (pg == null) return null;
+
+ PermissionGroupInfo pgi = new PermissionGroupInfo(
+ pg.getRequestDetailResourceId(),
+ pg.getBackgroundRequestResourceId(),
+ pg.getBackgroundRequestDetailResourceId()
+ );
+
+ assignSharedFieldsForPackageItemInfo(pgi, pg);
+ pgi.descriptionRes = pg.getDescriptionRes();
+ pgi.priority = pg.getPriority();
+ pgi.requestRes = pg.getRequestRes();
+ pgi.flags = pg.getFlags();
+
+ if ((flags & PackageManager.GET_META_DATA) == 0) {
+ return pgi;
+ }
+ pgi.metaData = pg.getMetaData();
+ return pgi;
+ }
+
+ private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
+ @NonNull ParsedMainComponent mainComponent) {
+ assignSharedFieldsForPackageItemInfo(componentInfo, mainComponent);
+ componentInfo.descriptionRes = mainComponent.getDescriptionRes();
+ componentInfo.directBootAware = mainComponent.isDirectBootAware();
+ componentInfo.enabled = mainComponent.isEnabled();
+ componentInfo.splitName = mainComponent.getSplitName();
+ }
+
+ private static void assignSharedFieldsForPackageItemInfo(
+ @NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component) {
+ packageItemInfo.nonLocalizedLabel = ComponentParseUtils.getNonLocalizedLabel(component);
+ packageItemInfo.icon = ComponentParseUtils.getIcon(component);
+
+ packageItemInfo.banner = component.getBanner();
+ packageItemInfo.labelRes = component.getLabelRes();
+ packageItemInfo.logo = component.getLogo();
+ packageItemInfo.name = component.getName();
+ packageItemInfo.packageName = component.getPackageName();
+ }
+
+ @CheckResult
+ private static int flag(boolean hasFlag, int flag) {
+ if (hasFlag) {
+ return flag;
+ } else {
+ return 0;
+ }
+ }
+
+ /** @see ApplicationInfo#flags */
+ public static int appInfoFlags(ParsingPackageRead pkg) {
+ // @formatter:off
+ return flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE)
+ | flag(pkg.isBaseHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED)
+ | flag(pkg.isAllowBackup(), ApplicationInfo.FLAG_ALLOW_BACKUP)
+ | flag(pkg.isKillAfterRestore(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
+ | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY)
+ | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT)
+ | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE)
+ | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE)
+ | flag(pkg.isHasCode(), ApplicationInfo.FLAG_HAS_CODE)
+ | flag(pkg.isAllowTaskReparenting(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
+ | flag(pkg.isAllowClearUserData(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)
+ | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP)
+ | flag(pkg.isUsesCleartextTraffic(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC)
+ | flag(pkg.isSupportsRtl(), ApplicationInfo.FLAG_SUPPORTS_RTL)
+ | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY)
+ | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH)
+ | flag(pkg.isExtractNativeLibs(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS)
+ | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME)
+ | flag(pkg.isSupportsSmallScreens(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS)
+ | flag(pkg.isSupportsNormalScreens(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS)
+ | flag(pkg.isSupportsLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS)
+ | flag(pkg.isSupportsExtraLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
+ | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
+ | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES);
+ // @formatter:on
+ }
+
+ /** @see ApplicationInfo#privateFlags */
+ public static int appInfoPrivateFlags(ParsingPackageRead pkg) {
+ // @formatter:off
+ int privateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY)
+ | flag(pkg.isOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY)
+ | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING)
+ | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS)
+ | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL)
+ | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND)
+ | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX)
+ | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE)
+ | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE)
+ | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE)
+ | flag(pkg.isAllowClearUserDataOnFailedRestore(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE)
+ | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE)
+ | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE)
+ | flag(pkg.isUsesNonSdkApi(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API)
+ | flag(pkg.isHasFragileUserData(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA)
+ | flag(pkg.isCantSaveState(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
+ | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
+ | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING);
+ // @formatter:on
+
+ Boolean resizeableActivity = pkg.getResizeableActivity();
+ if (resizeableActivity != null) {
+ if (resizeableActivity) {
+ privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
+ } else {
+ privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+ }
+ }
+
+ return privateFlags;
+ }
+
+ private static boolean checkUseInstalled(ParsingPackageRead pkg, PackageUserState state,
+ @PackageManager.PackageInfoFlags int flags) {
+ // If available for the target user
+ return state.isAvailable(flags);
+ }
+
+ @NonNull
+ public static File getDataDir(ParsingPackageRead pkg, int userId) {
+ if ("android".equals(pkg.getPackageName())) {
+ return Environment.getDataSystemDirectory();
+ }
+
+ if (pkg.isDefaultToDeviceProtectedStorage()
+ && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
+ return getDeviceProtectedDataDir(pkg, userId);
+ } else {
+ return getCredentialProtectedDataDir(pkg, userId);
+ }
+ }
+
+ @NonNull
+ public static File getDeviceProtectedDataDir(ParsingPackageRead pkg, int userId) {
+ return Environment.getDataUserDePackageDirectory(pkg.getVolumeUuid(), userId,
+ pkg.getPackageName());
+ }
+
+ @NonNull
+ public static File getCredentialProtectedDataDir(ParsingPackageRead pkg, int userId) {
+ return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId,
+ pkg.getPackageName());
+ }
+}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 74a2640..aa93d80 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -16,35 +16,36 @@
package android.content.pm.parsing;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
-import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
import android.os.Bundle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.SparseArray;
import java.security.PublicKey;
+import java.util.Map;
+import java.util.Set;
/**
* Methods used for mutation during direct package parsing.
*
- * Java disallows defining this as an inner interface, so this must be a separate file.
- *
* @hide
*/
-public interface ParsingPackage extends AndroidPackage {
+@SuppressWarnings("UnusedReturnValue")
+public interface ParsingPackage extends ParsingPackageRead {
ParsingPackage addActivity(ParsedActivity parsedActivity);
@@ -66,18 +67,18 @@
ParsingPackage addOverlayable(String overlayableName, String actorName);
- ParsingPackage addFeature(ParsedFeature permission);
-
ParsingPackage addPermission(ParsedPermission permission);
ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
- ParsingPackage addPreferredActivityFilter(ParsedActivityIntentInfo activityIntentInfo);
+ ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo);
ParsingPackage addProtectedBroadcast(String protectedBroadcast);
ParsingPackage addProvider(ParsedProvider parsedProvider);
+ ParsingPackage addFeature(ParsedFeature permission);
+
ParsingPackage addReceiver(ParsedActivity parsedReceiver);
ParsingPackage addReqFeature(FeatureInfo reqFeature);
@@ -102,7 +103,7 @@
ParsingPackage addQueriesProvider(String authority);
- ParsingPackage setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes);
+ ParsingPackage setProcesses(@NonNull Map<String, ParsedProcess> processes);
ParsingPackage asSplit(
String[] splitNames,
@@ -111,7 +112,7 @@
@Nullable SparseArray<int[]> splitDependencies
);
- ParsingPackage setAppMetaData(Bundle appMetaData);
+ ParsingPackage setMetaData(Bundle metaData);
ParsingPackage setForceQueryable(boolean forceQueryable);
@@ -119,8 +120,6 @@
ParsingPackage setMinAspectRatio(float minAspectRatio);
- ParsingPackage setName(String name);
-
ParsingPackage setPermission(String permission);
ParsingPackage setProcessName(String processName);
@@ -137,9 +136,9 @@
ParsingPackage setBaseHardwareAccelerated(boolean baseHardwareAccelerated);
- ParsingPackage setActivitiesResizeModeResizeable(boolean resizeable);
+ ParsingPackage setResizeableActivity(Boolean resizeable);
- ParsingPackage setActivitiesResizeModeResizeableViaSdkVersion(boolean resizeableViaSdkVersion);
+ ParsingPackage setResizeableActivityViaSdkVersion(boolean resizeableViaSdkVersion);
ParsingPackage setAllowAudioPlaybackCapture(boolean allowAudioPlaybackCapture);
@@ -151,7 +150,7 @@
ParsingPackage setAllowTaskReparenting(boolean allowTaskReparenting);
- ParsingPackage setIsOverlay(boolean isOverlay);
+ ParsingPackage setOverlay(boolean isOverlay);
ParsingPackage setBackupInForeground(boolean backupInForeground);
@@ -173,7 +172,7 @@
ParsingPackage setHasFragileUserData(boolean hasFragileUserData);
- ParsingPackage setIsGame(boolean isGame);
+ ParsingPackage setGame(boolean isGame);
ParsingPackage setIsolatedSplitLoading(boolean isolatedSplitLoading);
@@ -221,8 +220,6 @@
ParsingPackage setAppComponentFactory(String appComponentFactory);
- ParsingPackage setApplicationVolumeUuid(String applicationVolumeUuid);
-
ParsingPackage setBackupAgentName(String backupAgentName);
ParsingPackage setBanner(int banner);
@@ -233,8 +230,6 @@
ParsingPackage setClassName(String className);
- ParsingPackage setCodePath(String codePath);
-
ParsingPackage setCompatibleWidthLimitDp(int compatibleWidthLimitDp);
ParsingPackage setDescriptionRes(int descriptionRes);
@@ -247,8 +242,6 @@
ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
- ParsingPackage setIcon(int icon);
-
ParsingPackage setIconRes(int iconRes);
ParsingPackage setInstallLocation(int installLocation);
@@ -307,17 +300,17 @@
ParsingPackage setSupportsSmallScreens(int supportsSmallScreens);
- ParsingPackage setSupportsXLargeScreens(int supportsXLargeScreens);
+ ParsingPackage setSupportsExtraLargeScreens(int supportsExtraLargeScreens);
ParsingPackage setTargetSandboxVersion(int targetSandboxVersion);
ParsingPackage setTheme(int theme);
- ParsingPackage setUpgradeKeySets(ArraySet<String> upgradeKeySets);
+ ParsingPackage setUpgradeKeySets(@NonNull Set<String> upgradeKeySets);
ParsingPackage setUse32BitAbi(boolean use32BitAbi);
- ParsingPackage setVolumeUuid(String volumeUuid);
+ ParsingPackage setVolumeUuid(@Nullable String volumeUuid);
ParsingPackage setZygotePreloadName(String zygotePreloadName);
@@ -327,17 +320,16 @@
ParsingPackage sortServices();
- ParsedPackage hideAsParsed();
-
ParsingPackage setBaseRevisionCode(int baseRevisionCode);
- ParsingPackage setPreferredOrder(int preferredOrder);
-
ParsingPackage setVersionName(String versionName);
ParsingPackage setCompileSdkVersion(int compileSdkVersion);
ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename);
- boolean usesCompatibilityMode();
+ // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
+ // for moving to the next step
+ @Deprecated
+ Object hideAsParsed();
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
new file mode 100644
index 0000000..c3eea2b
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -0,0 +1,2584 @@
+/*
+ * Copyright (C) 2020 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.parsing;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForBoolean;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArray;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringList;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedStringValueMap;
+
+import java.security.PublicKey;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The backing data for a package that was parsed from disk.
+ *
+ * The field nullability annotations here are for internal reference. For effective nullability,
+ * see the parent interfaces.
+ *
+ * TODO(b/135203078): Convert Lists used as sets into Sets, to better express intended use case
+ *
+ * @hide
+ */
+public class ParsingPackageImpl implements ParsingPackage, Parcelable {
+
+ private static final String TAG = "PackageImpl";
+
+ public static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+ public static ForInternedString sForString = Parcelling.Cache.getOrCreate(
+ ForInternedString.class);
+ public static ForInternedStringArray sForStringArray = Parcelling.Cache.getOrCreate(
+ ForInternedStringArray.class);
+ public static ForInternedStringList sForStringList = Parcelling.Cache.getOrCreate(
+ ForInternedStringList.class);
+ public static ForInternedStringValueMap sForStringValueMap = Parcelling.Cache.getOrCreate(
+ ForInternedStringValueMap.class);
+ public static ForInternedStringSet sForStringSet = Parcelling.Cache.getOrCreate(
+ ForInternedStringSet.class);
+ protected static ParsedIntentInfo.StringPairListParceler sForIntentInfoPairs =
+ Parcelling.Cache.getOrCreate(ParsedIntentInfo.StringPairListParceler.class);
+
+ private static final Comparator<ParsedMainComponent> ORDER_COMPARATOR =
+ (first, second) -> Integer.compare(second.getOrder(), first.getOrder());
+
+ // These are objects because null represents not explicitly set
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean supportsSmallScreens;
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean supportsNormalScreens;
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean supportsLargeScreens;
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean supportsExtraLargeScreens;
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean resizeable;
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean anyDensity;
+
+ protected int versionCode;
+ protected int versionCodeMajor;
+ private int baseRevisionCode;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String versionName;
+
+ private int compileSdkVersion;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String compileSdkVersionCodeName;
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String packageName;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String realPackage;
+
+ @NonNull
+ protected String baseCodePath;
+
+ private boolean requiredForAllUsers;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String restrictedAccountType;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String requiredAccountType;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String overlayTarget;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String overlayTargetName;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String overlayCategory;
+ private int overlayPriority;
+ private boolean overlayIsStatic;
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringValueMap.class)
+ private Map<String, String> overlayables = emptyMap();
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String staticSharedLibName;
+ private long staticSharedLibVersion;
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> libraryNames = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesLibraries = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> usesOptionalLibraries = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> usesStaticLibraries = emptyList();
+ @Nullable
+ private long[] usesStaticLibrariesVersions;
+
+ @Nullable
+ private String[][] usesStaticLibrariesCertDigests;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String sharedUserId;
+
+ private int sharedUserLabel;
+ @NonNull
+ private List<ConfigurationInfo> configPreferences = emptyList();
+ @NonNull
+ private List<FeatureInfo> reqFeatures = emptyList();
+ @NonNull
+ private List<FeatureGroupInfo> featureGroups = emptyList();
+
+ @Nullable
+ private byte[] restrictUpdateHash;
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> originalPackages = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> adoptPermissions = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> requestedPermissions = emptyList();
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> implicitPermissions = emptyList();
+
+ @NonNull
+ private Set<String> upgradeKeySets = emptySet();
+ @NonNull
+ private Map<String, ArraySet<PublicKey>> keySetMapping = emptyMap();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ protected List<String> protectedBroadcasts = emptyList();
+
+ @NonNull
+ protected List<ParsedActivity> activities = emptyList();
+
+ @NonNull
+ protected List<ParsedActivity> receivers = emptyList();
+
+ @NonNull
+ protected List<ParsedService> services = emptyList();
+
+ @NonNull
+ protected List<ParsedProvider> providers = emptyList();
+
+ @NonNull
+ private List<ParsedFeature> features = emptyList();
+
+ @NonNull
+ protected List<ParsedPermission> permissions = emptyList();
+
+ @NonNull
+ protected List<ParsedPermissionGroup> permissionGroups = emptyList();
+
+ @NonNull
+ protected List<ParsedInstrumentation> instrumentations = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
+ private List<Pair<String, ParsedIntentInfo>> preferredActivityFilters = emptyList();
+
+ @NonNull
+ private Map<String, ParsedProcess> processes = emptyMap();
+
+ @Nullable
+ private Bundle metaData;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String volumeUuid;
+ @Nullable
+ private PackageParser.SigningDetails signingDetails;
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String codePath;
+
+ private boolean use32BitAbi;
+ private boolean visibleToInstantApps;
+
+ private boolean forceQueryable;
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<Intent> queriesIntents = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> queriesPackages = emptyList();
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringSet.class)
+ private Set<String> queriesProviders = emptySet();
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedStringArray.class)
+ private String[] splitClassLoaderNames;
+ @Nullable
+ protected String[] splitCodePaths;
+ @Nullable
+ private SparseArray<int[]> splitDependencies;
+ @Nullable
+ private int[] splitFlags;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedStringArray.class)
+ private String[] splitNames;
+ @Nullable
+ private int[] splitRevisionCodes;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String appComponentFactory;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String backupAgentName;
+ private int banner;
+ private int category;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String classLoaderName;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String className;
+ private int compatibleWidthLimitDp;
+ private int descriptionRes;
+ private boolean enabled;
+ private boolean crossProfile;
+ private int fullBackupContent;
+ private int iconRes;
+ private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
+ private int labelRes;
+ private int largestWidthLimitDp;
+ private int logo;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String manageSpaceActivityName;
+ private float maxAspectRatio;
+ private float minAspectRatio;
+ private int minSdkVersion;
+ private int networkSecurityConfigRes;
+ @Nullable
+ private CharSequence nonLocalizedLabel;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String permission;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String processName;
+ private int requiresSmallestWidthDp;
+ private int roundIconRes;
+ private int targetSandboxVersion;
+ private int targetSdkVersion;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String taskAffinity;
+ private int theme;
+
+ private int uiOptions;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String zygotePreloadName;
+
+ private boolean externalStorage;
+ private boolean baseHardwareAccelerated;
+ private boolean allowBackup;
+ private boolean killAfterRestore;
+ private boolean restoreAnyVersion;
+ private boolean fullBackupOnly;
+ private boolean persistent;
+ private boolean debuggable;
+ private boolean vmSafeMode;
+ private boolean hasCode;
+ private boolean allowTaskReparenting;
+ private boolean allowClearUserData;
+ private boolean largeHeap;
+ private boolean usesCleartextTraffic;
+ private boolean supportsRtl;
+ private boolean testOnly;
+ private boolean multiArch;
+ private boolean extractNativeLibs;
+ private boolean game;
+
+ /**
+ * @see ParsingPackageRead#getResizeableActivity()
+ */
+ @Nullable
+ @DataClass.ParcelWith(ForBoolean.class)
+ private Boolean resizeableActivity;
+
+ private boolean staticSharedLibrary;
+ private boolean overlay;
+ private boolean isolatedSplitLoading;
+ private boolean hasDomainUrls;
+ private boolean profileableByShell;
+ private boolean backupInForeground;
+ private boolean useEmbeddedDex;
+ private boolean defaultToDeviceProtectedStorage;
+ private boolean directBootAware;
+ private boolean partiallyDirectBootAware;
+ private boolean resizeableActivityViaSdkVersion;
+ private boolean allowClearUserDataOnFailedRestore;
+ private boolean allowAudioPlaybackCapture;
+ private boolean requestLegacyExternalStorage;
+ private boolean usesNonSdkApi;
+ private boolean hasFragileUserData;
+ private boolean cantSaveState;
+ private boolean allowNativeHeapPointerTagging;
+ private boolean preserveLegacyExternalStorage;
+
+ // TODO(chiuwinson): Non-null
+ @Nullable
+ private ArraySet<String> mimeGroups;
+
+ @VisibleForTesting
+ public ParsingPackageImpl(@NonNull String packageName, @NonNull String baseCodePath,
+ @NonNull String codePath, @Nullable TypedArray manifestArray) {
+ this.packageName = TextUtils.safeIntern(packageName);
+ this.baseCodePath = TextUtils.safeIntern(baseCodePath);
+ this.codePath = TextUtils.safeIntern(codePath);
+
+ if (manifestArray != null) {
+ versionCode = manifestArray.getInteger(R.styleable.AndroidManifest_versionCode, 0);
+ versionCodeMajor = manifestArray.getInteger(
+ R.styleable.AndroidManifest_versionCodeMajor, 0);
+ setBaseRevisionCode(
+ manifestArray.getInteger(R.styleable.AndroidManifest_revisionCode, 0));
+ setVersionName(manifestArray.getNonConfigurationString(
+ R.styleable.AndroidManifest_versionName, 0));
+
+ setCompileSdkVersion(manifestArray.getInteger(
+ R.styleable.AndroidManifest_compileSdkVersion, 0));
+ setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
+ R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
+ }
+ }
+
+ public boolean isSupportsSmallScreens() {
+ if (supportsSmallScreens == null) {
+ return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+ }
+
+ return supportsSmallScreens;
+ }
+
+ public boolean isSupportsNormalScreens() {
+ return supportsNormalScreens == null || supportsNormalScreens;
+ }
+
+ public boolean isSupportsLargeScreens() {
+ if (supportsLargeScreens == null) {
+ return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+ }
+
+ return supportsLargeScreens;
+ }
+
+ public boolean isSupportsExtraLargeScreens() {
+ if (supportsExtraLargeScreens == null) {
+ return targetSdkVersion >= Build.VERSION_CODES.GINGERBREAD;
+ }
+
+ return supportsExtraLargeScreens;
+ }
+
+ public boolean isResizeable() {
+ if (resizeable == null) {
+ return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+ }
+
+ return resizeable;
+ }
+
+ public boolean isAnyDensity() {
+ if (anyDensity == null) {
+ return targetSdkVersion >= Build.VERSION_CODES.DONUT;
+ }
+
+ return anyDensity;
+ }
+
+ @Override
+ public ParsingPackageImpl sortActivities() {
+ Collections.sort(this.activities, ORDER_COMPARATOR);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl sortReceivers() {
+ Collections.sort(this.receivers, ORDER_COMPARATOR);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl sortServices() {
+ Collections.sort(this.services, ORDER_COMPARATOR);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setVersionName(String versionName) {
+ this.versionName = TextUtils.safeIntern(versionName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackage setCompileSdkVersionCodename(String compileSdkVersionCodename) {
+ this.compileSdkVersionCodeName = TextUtils.safeIntern(compileSdkVersionCodename);
+ return this;
+ }
+
+ @Override
+ public Object hideAsParsed() {
+ // There is no equivalent for core-only parsing
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ParsingPackageImpl addConfigPreference(ConfigurationInfo configPreference) {
+ this.configPreferences = CollectionUtils.add(this.configPreferences, configPreference);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addReqFeature(FeatureInfo reqFeature) {
+ this.reqFeatures = CollectionUtils.add(this.reqFeatures, reqFeature);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addFeatureGroup(FeatureGroupInfo featureGroup) {
+ this.featureGroups = CollectionUtils.add(this.featureGroups, featureGroup);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) {
+ if (!this.protectedBroadcasts.contains(protectedBroadcast)) {
+ this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts,
+ TextUtils.safeIntern(protectedBroadcast));
+ }
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addInstrumentation(ParsedInstrumentation instrumentation) {
+ this.instrumentations = CollectionUtils.add(this.instrumentations, instrumentation);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addOriginalPackage(String originalPackage) {
+ this.originalPackages = CollectionUtils.add(this.originalPackages,
+ TextUtils.safeIntern(originalPackage));
+ return this;
+ }
+
+ @Override
+ public ParsingPackage addOverlayable(String overlayableName, String actorName) {
+ this.overlayables = CollectionUtils.add(this.overlayables,
+ TextUtils.safeIntern(overlayableName), TextUtils.safeIntern(actorName));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addAdoptPermission(String adoptPermission) {
+ this.adoptPermissions = CollectionUtils.add(this.adoptPermissions,
+ TextUtils.safeIntern(adoptPermission));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addPermission(ParsedPermission permission) {
+ this.permissions = CollectionUtils.add(this.permissions, permission);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addPermissionGroup(ParsedPermissionGroup permissionGroup) {
+ this.permissionGroups = CollectionUtils.add(this.permissionGroups, permissionGroup);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addRequestedPermission(String permission) {
+ this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
+ TextUtils.safeIntern(permission));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addImplicitPermission(String permission) {
+ this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
+ TextUtils.safeIntern(permission));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addKeySet(String keySetName, PublicKey publicKey) {
+ ArraySet<PublicKey> publicKeys = keySetMapping.get(keySetName);
+ if (publicKeys == null) {
+ publicKeys = new ArraySet<>();
+ }
+ publicKeys.add(publicKey);
+ keySetMapping = CollectionUtils.add(this.keySetMapping, keySetName, publicKeys);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addActivity(ParsedActivity parsedActivity) {
+ this.activities = CollectionUtils.add(this.activities, parsedActivity);
+ addMimeGroupsFromComponent(parsedActivity);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {
+ this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);
+ addMimeGroupsFromComponent(parsedReceiver);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addService(ParsedService parsedService) {
+ this.services = CollectionUtils.add(this.services, parsedService);
+ addMimeGroupsFromComponent(parsedService);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addProvider(ParsedProvider parsedProvider) {
+ this.providers = CollectionUtils.add(this.providers, parsedProvider);
+ addMimeGroupsFromComponent(parsedProvider);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addFeature(ParsedFeature feature) {
+ this.features = CollectionUtils.add(this.features, feature);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addLibraryName(String libraryName) {
+ this.libraryNames = CollectionUtils.add(this.libraryNames,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesOptionalLibrary(String libraryName) {
+ this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesLibrary(String libraryName) {
+ this.usesLibraries = CollectionUtils.add(this.usesLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl removeUsesOptionalLibrary(String libraryName) {
+ this.usesOptionalLibraries = CollectionUtils.remove(this.usesOptionalLibraries,
+ libraryName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
+ this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesStaticLibraryVersion(long version) {
+ this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
+ version, true);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) {
+ this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+ this.usesStaticLibrariesCertDigests, certSha256Digests, true);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addPreferredActivityFilter(String className,
+ ParsedIntentInfo intentInfo) {
+ this.preferredActivityFilters = CollectionUtils.add(this.preferredActivityFilters,
+ Pair.create(className, intentInfo));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addQueriesIntent(Intent intent) {
+ this.queriesIntents = CollectionUtils.add(this.queriesIntents, intent);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addQueriesPackage(String packageName) {
+ this.queriesPackages = CollectionUtils.add(this.queriesPackages,
+ TextUtils.safeIntern(packageName));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addQueriesProvider(String authority) {
+ this.queriesProviders = CollectionUtils.add(this.queriesProviders,
+ TextUtils.safeIntern(authority));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSupportsSmallScreens(int supportsSmallScreens) {
+ if (supportsSmallScreens == 1) {
+ return this;
+ }
+
+ this.supportsSmallScreens = supportsSmallScreens < 0;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSupportsNormalScreens(int supportsNormalScreens) {
+ if (supportsNormalScreens == 1) {
+ return this;
+ }
+
+ this.supportsNormalScreens = supportsNormalScreens < 0;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSupportsLargeScreens(int supportsLargeScreens) {
+ if (supportsLargeScreens == 1) {
+ return this;
+ }
+
+ this.supportsLargeScreens = supportsLargeScreens < 0;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSupportsExtraLargeScreens(int supportsExtraLargeScreens) {
+ if (supportsExtraLargeScreens == 1) {
+ return this;
+ }
+
+ this.supportsExtraLargeScreens = supportsExtraLargeScreens < 0;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setResizeable(int resizeable) {
+ if (resizeable == 1) {
+ return this;
+ }
+
+ this.resizeable = resizeable < 0;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAnyDensity(int anyDensity) {
+ if (anyDensity == 1) {
+ return this;
+ }
+
+ this.anyDensity = anyDensity < 0;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl asSplit(
+ String[] splitNames,
+ String[] splitCodePaths,
+ int[] splitRevisionCodes,
+ SparseArray<int[]> splitDependencies
+ ) {
+ this.splitNames = splitNames;
+
+ if (this.splitNames != null) {
+ for (int index = 0; index < this.splitNames.length; index++) {
+ splitNames[index] = TextUtils.safeIntern(splitNames[index]);
+ }
+ }
+
+ this.splitCodePaths = splitCodePaths;
+ this.splitRevisionCodes = splitRevisionCodes;
+ this.splitDependencies = splitDependencies;
+
+ int count = splitNames.length;
+ this.splitFlags = new int[count];
+ this.splitClassLoaderNames = new String[count];
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSplitHasCode(int splitIndex, boolean splitHasCode) {
+ this.splitFlags[splitIndex] = splitHasCode
+ ? this.splitFlags[splitIndex] | ApplicationInfo.FLAG_HAS_CODE
+ : this.splitFlags[splitIndex] & ~ApplicationInfo.FLAG_HAS_CODE;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSplitClassLoaderName(int splitIndex, String classLoaderName) {
+ this.splitClassLoaderNames[splitIndex] = classLoaderName;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setProcessName(String processName) {
+ this.processName = TextUtils.safeIntern(processName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRealPackage(@Nullable String realPackage) {
+ this.realPackage = TextUtils.safeIntern(realPackage);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRestrictedAccountType(@Nullable String restrictedAccountType) {
+ this.restrictedAccountType = TextUtils.safeIntern(restrictedAccountType);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRequiredAccountType(@Nullable String requiredAccountType) {
+ this.requiredAccountType = TextUtils.nullIfEmpty(TextUtils.safeIntern(requiredAccountType));
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setOverlayTarget(@Nullable String overlayTarget) {
+ this.overlayTarget = TextUtils.safeIntern(overlayTarget);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setOverlayTargetName(@Nullable String overlayTargetName) {
+ this.overlayTargetName = TextUtils.safeIntern(overlayTargetName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setOverlayCategory(@Nullable String overlayCategory) {
+ this.overlayCategory = TextUtils.safeIntern(overlayCategory);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setVolumeUuid(@Nullable String volumeUuid) {
+ this.volumeUuid = TextUtils.safeIntern(volumeUuid);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAppComponentFactory(@Nullable String appComponentFactory) {
+ this.appComponentFactory = TextUtils.safeIntern(appComponentFactory);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setBackupAgentName(@Nullable String backupAgentName) {
+ this.backupAgentName = TextUtils.safeIntern(backupAgentName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setClassLoaderName(@Nullable String classLoaderName) {
+ this.classLoaderName = TextUtils.safeIntern(classLoaderName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setClassName(@Nullable String className) {
+ this.className = TextUtils.safeIntern(className);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setManageSpaceActivityName(@Nullable String manageSpaceActivityName) {
+ this.manageSpaceActivityName = TextUtils.safeIntern(manageSpaceActivityName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setPermission(@Nullable String permission) {
+ this.permission = TextUtils.safeIntern(permission);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setTaskAffinity(@Nullable String taskAffinity) {
+ this.taskAffinity = TextUtils.safeIntern(taskAffinity);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setZygotePreloadName(@Nullable String zygotePreloadName) {
+ this.zygotePreloadName = TextUtils.safeIntern(zygotePreloadName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setStaticSharedLibName(String staticSharedLibName) {
+ this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSharedUserId(String sharedUserId) {
+ this.sharedUserId = TextUtils.safeIntern(sharedUserId);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public String getProcessName() {
+ return processName != null ? processName : packageName;
+ }
+
+ @Override
+ public String toString() {
+ return "Package{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + packageName + "}";
+ }
+
+ @Deprecated
+ @Override
+ public ApplicationInfo toAppInfoWithoutState() {
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.flags = PackageInfoWithoutStateUtils.appInfoFlags(this);
+ appInfo.privateFlags = PackageInfoWithoutStateUtils.appInfoPrivateFlags(this);
+
+ appInfo.appComponentFactory = appComponentFactory;
+ appInfo.backupAgentName = backupAgentName;
+ appInfo.banner = banner;
+ appInfo.category = category;
+ appInfo.classLoaderName = classLoaderName;
+ appInfo.className = className;
+ appInfo.compatibleWidthLimitDp = compatibleWidthLimitDp;
+ appInfo.compileSdkVersion = compileSdkVersion;
+ appInfo.compileSdkVersionCodename = compileSdkVersionCodeName;
+// appInfo.credentialProtectedDataDir = credentialProtectedDataDir;
+// appInfo.dataDir = dataDir;
+ appInfo.descriptionRes = descriptionRes;
+// appInfo.deviceProtectedDataDir = deviceProtectedDataDir;
+ appInfo.enabled = enabled;
+ appInfo.fullBackupContent = fullBackupContent;
+// appInfo.hiddenUntilInstalled = hiddenUntilInstalled;
+ appInfo.icon = (PackageParser.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
+ appInfo.iconRes = iconRes;
+ appInfo.roundIconRes = roundIconRes;
+ appInfo.installLocation = installLocation;
+ appInfo.labelRes = labelRes;
+ appInfo.largestWidthLimitDp = largestWidthLimitDp;
+ appInfo.logo = logo;
+ appInfo.manageSpaceActivityName = manageSpaceActivityName;
+ appInfo.maxAspectRatio = maxAspectRatio;
+ appInfo.metaData = metaData;
+ appInfo.minAspectRatio = minAspectRatio;
+ appInfo.minSdkVersion = minSdkVersion;
+ appInfo.name = className;
+ if (appInfo.name != null) {
+ appInfo.name = appInfo.name.trim();
+ }
+// appInfo.nativeLibraryDir = nativeLibraryDir;
+// appInfo.nativeLibraryRootDir = nativeLibraryRootDir;
+// appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+ appInfo.networkSecurityConfigRes = networkSecurityConfigRes;
+ appInfo.nonLocalizedLabel = nonLocalizedLabel;
+ if (appInfo.nonLocalizedLabel != null) {
+ appInfo.nonLocalizedLabel = appInfo.nonLocalizedLabel.toString().trim();
+ }
+ appInfo.packageName = packageName;
+ appInfo.permission = permission;
+// appInfo.primaryCpuAbi = primaryCpuAbi;
+ appInfo.processName = getProcessName();
+ appInfo.requiresSmallestWidthDp = requiresSmallestWidthDp;
+// appInfo.secondaryCpuAbi = secondaryCpuAbi;
+// appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+// appInfo.seInfo = seInfo;
+// appInfo.seInfoUser = seInfoUser;
+// appInfo.sharedLibraryFiles = usesLibraryFiles.isEmpty()
+// ? null : usesLibraryFiles.toArray(new String[0]);
+// appInfo.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+ appInfo.splitClassLoaderNames = splitClassLoaderNames;
+ appInfo.splitDependencies = splitDependencies;
+ appInfo.splitNames = splitNames;
+ appInfo.storageUuid = StorageManager.convert(volumeUuid);
+ appInfo.targetSandboxVersion = targetSandboxVersion;
+ appInfo.targetSdkVersion = targetSdkVersion;
+ appInfo.taskAffinity = taskAffinity;
+ appInfo.theme = theme;
+// appInfo.uid = uid;
+ appInfo.uiOptions = uiOptions;
+ appInfo.volumeUuid = volumeUuid;
+ appInfo.zygotePreloadName = zygotePreloadName;
+ appInfo.crossProfile = isCrossProfile();
+
+ appInfo.setBaseCodePath(baseCodePath);
+ appInfo.setBaseResourcePath(baseCodePath);
+ appInfo.setCodePath(codePath);
+ appInfo.setResourcePath(codePath);
+ appInfo.setSplitCodePaths(splitCodePaths);
+ appInfo.setSplitResourcePaths(splitCodePaths);
+ appInfo.setVersionCode(PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode));
+
+ // TODO(b/135203078): Can this be removed? Looks only used in ActivityInfo.
+// appInfo.showUserIcon = pkg.getShowUserIcon();
+ // TODO(b/135203078): Unused?
+// appInfo.resourceDirs = pkg.getResourceDirs();
+ // TODO(b/135203078): Unused?
+// appInfo.enabledSetting = pkg.getEnabledSetting();
+ // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
+// appInfo.mHiddenApiPolicy = pkg.getHiddenApiPolicy();
+
+ return appInfo;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ sForBoolean.parcel(this.supportsSmallScreens, dest, flags);
+ sForBoolean.parcel(this.supportsNormalScreens, dest, flags);
+ sForBoolean.parcel(this.supportsLargeScreens, dest, flags);
+ sForBoolean.parcel(this.supportsExtraLargeScreens, dest, flags);
+ sForBoolean.parcel(this.resizeable, dest, flags);
+ sForBoolean.parcel(this.anyDensity, dest, flags);
+ dest.writeInt(this.versionCode);
+ dest.writeInt(this.versionCodeMajor);
+ dest.writeInt(this.baseRevisionCode);
+ sForString.parcel(this.versionName, dest, flags);
+ dest.writeInt(this.compileSdkVersion);
+ sForString.parcel(this.compileSdkVersionCodeName, dest, flags);
+ sForString.parcel(this.packageName, dest, flags);
+ sForString.parcel(this.realPackage, dest, flags);
+ sForString.parcel(this.baseCodePath, dest, flags);
+ dest.writeBoolean(this.requiredForAllUsers);
+ sForString.parcel(this.restrictedAccountType, dest, flags);
+ sForString.parcel(this.requiredAccountType, dest, flags);
+ sForString.parcel(this.overlayTarget, dest, flags);
+ sForString.parcel(this.overlayTargetName, dest, flags);
+ sForString.parcel(this.overlayCategory, dest, flags);
+ dest.writeInt(this.overlayPriority);
+ dest.writeBoolean(this.overlayIsStatic);
+ sForStringValueMap.parcel(this.overlayables, dest, flags);
+ sForString.parcel(this.staticSharedLibName, dest, flags);
+ dest.writeLong(this.staticSharedLibVersion);
+ sForStringList.parcel(this.libraryNames, dest, flags);
+ sForStringList.parcel(this.usesLibraries, dest, flags);
+ sForStringList.parcel(this.usesOptionalLibraries, dest, flags);
+ sForStringList.parcel(this.usesStaticLibraries, dest, flags);
+ dest.writeLongArray(this.usesStaticLibrariesVersions);
+
+ if (this.usesStaticLibrariesCertDigests == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(this.usesStaticLibrariesCertDigests.length);
+ for (int index = 0; index < this.usesStaticLibrariesCertDigests.length; index++) {
+ sForStringArray.parcel(this.usesStaticLibrariesCertDigests[index], dest, flags);
+ }
+ }
+
+ sForString.parcel(this.sharedUserId, dest, flags);
+ dest.writeInt(this.sharedUserLabel);
+ dest.writeTypedList(this.configPreferences);
+ dest.writeTypedList(this.reqFeatures);
+ dest.writeTypedList(this.featureGroups);
+ dest.writeByteArray(this.restrictUpdateHash);
+ sForStringList.parcel(this.originalPackages, dest, flags);
+ sForStringList.parcel(this.adoptPermissions, dest, flags);
+ sForStringList.parcel(this.requestedPermissions, dest, flags);
+ sForStringList.parcel(this.implicitPermissions, dest, flags);
+ sForStringSet.parcel(this.upgradeKeySets, dest, flags);
+ dest.writeMap(this.keySetMapping);
+ sForStringList.parcel(this.protectedBroadcasts, dest, flags);
+ dest.writeTypedList(this.activities);
+ dest.writeTypedList(this.receivers);
+ dest.writeTypedList(this.services);
+ dest.writeTypedList(this.providers);
+ dest.writeTypedList(this.features);
+ dest.writeTypedList(this.permissions);
+ dest.writeTypedList(this.permissionGroups);
+ dest.writeTypedList(this.instrumentations);
+ sForIntentInfoPairs.parcel(this.preferredActivityFilters, dest, flags);
+ dest.writeMap(this.processes);
+ dest.writeBundle(this.metaData);
+ sForString.parcel(this.volumeUuid, dest, flags);
+ dest.writeParcelable(this.signingDetails, flags);
+ sForString.parcel(this.codePath, dest, flags);
+ dest.writeBoolean(this.use32BitAbi);
+ dest.writeBoolean(this.visibleToInstantApps);
+ dest.writeBoolean(this.forceQueryable);
+ dest.writeParcelableList(this.queriesIntents, flags);
+ sForStringList.parcel(this.queriesPackages, dest, flags);
+ sForString.parcel(this.appComponentFactory, dest, flags);
+ sForString.parcel(this.backupAgentName, dest, flags);
+ dest.writeInt(this.banner);
+ dest.writeInt(this.category);
+ sForString.parcel(this.classLoaderName, dest, flags);
+ sForString.parcel(this.className, dest, flags);
+ dest.writeInt(this.compatibleWidthLimitDp);
+ dest.writeInt(this.descriptionRes);
+ dest.writeBoolean(this.enabled);
+ dest.writeBoolean(this.crossProfile);
+ dest.writeInt(this.fullBackupContent);
+ dest.writeInt(this.iconRes);
+ dest.writeInt(this.installLocation);
+ dest.writeInt(this.labelRes);
+ dest.writeInt(this.largestWidthLimitDp);
+ dest.writeInt(this.logo);
+ sForString.parcel(this.manageSpaceActivityName, dest, flags);
+ dest.writeFloat(this.maxAspectRatio);
+ dest.writeFloat(this.minAspectRatio);
+ dest.writeInt(this.minSdkVersion);
+ dest.writeInt(this.networkSecurityConfigRes);
+ dest.writeCharSequence(this.nonLocalizedLabel);
+ sForString.parcel(this.permission, dest, flags);
+ sForString.parcel(this.processName, dest, flags);
+ dest.writeInt(this.requiresSmallestWidthDp);
+ dest.writeInt(this.roundIconRes);
+ dest.writeInt(this.targetSandboxVersion);
+ dest.writeInt(this.targetSdkVersion);
+ sForString.parcel(this.taskAffinity, dest, flags);
+ dest.writeInt(this.theme);
+ dest.writeInt(this.uiOptions);
+ sForString.parcel(this.zygotePreloadName, dest, flags);
+ sForStringArray.parcel(this.splitClassLoaderNames, dest, flags);
+ sForStringArray.parcel(this.splitCodePaths, dest, flags);
+ dest.writeSparseArray(this.splitDependencies);
+ dest.writeIntArray(this.splitFlags);
+ sForStringArray.parcel(this.splitNames, dest, flags);
+ dest.writeIntArray(this.splitRevisionCodes);
+
+ dest.writeBoolean(this.externalStorage);
+ dest.writeBoolean(this.baseHardwareAccelerated);
+ dest.writeBoolean(this.allowBackup);
+ dest.writeBoolean(this.killAfterRestore);
+ dest.writeBoolean(this.restoreAnyVersion);
+ dest.writeBoolean(this.fullBackupOnly);
+ dest.writeBoolean(this.persistent);
+ dest.writeBoolean(this.debuggable);
+ dest.writeBoolean(this.vmSafeMode);
+ dest.writeBoolean(this.hasCode);
+ dest.writeBoolean(this.allowTaskReparenting);
+ dest.writeBoolean(this.allowClearUserData);
+ dest.writeBoolean(this.largeHeap);
+ dest.writeBoolean(this.usesCleartextTraffic);
+ dest.writeBoolean(this.supportsRtl);
+ dest.writeBoolean(this.testOnly);
+ dest.writeBoolean(this.multiArch);
+ dest.writeBoolean(this.extractNativeLibs);
+ dest.writeBoolean(this.game);
+
+ sForBoolean.parcel(this.resizeableActivity, dest, flags);
+
+ dest.writeBoolean(this.staticSharedLibrary);
+ dest.writeBoolean(this.overlay);
+ dest.writeBoolean(this.isolatedSplitLoading);
+ dest.writeBoolean(this.hasDomainUrls);
+ dest.writeBoolean(this.profileableByShell);
+ dest.writeBoolean(this.backupInForeground);
+ dest.writeBoolean(this.useEmbeddedDex);
+ dest.writeBoolean(this.defaultToDeviceProtectedStorage);
+ dest.writeBoolean(this.directBootAware);
+ dest.writeBoolean(this.partiallyDirectBootAware);
+ dest.writeBoolean(this.resizeableActivityViaSdkVersion);
+ dest.writeBoolean(this.allowClearUserDataOnFailedRestore);
+ dest.writeBoolean(this.allowAudioPlaybackCapture);
+ dest.writeBoolean(this.requestLegacyExternalStorage);
+ dest.writeBoolean(this.usesNonSdkApi);
+ dest.writeBoolean(this.hasFragileUserData);
+ dest.writeBoolean(this.cantSaveState);
+ dest.writeBoolean(this.allowNativeHeapPointerTagging);
+ dest.writeBoolean(this.preserveLegacyExternalStorage);
+ dest.writeArraySet(this.mimeGroups);
+ }
+
+ public ParsingPackageImpl(Parcel in) {
+ // We use the boot classloader for all classes that we load.
+ final ClassLoader boot = Object.class.getClassLoader();
+ this.supportsSmallScreens = sForBoolean.unparcel(in);
+ this.supportsNormalScreens = sForBoolean.unparcel(in);
+ this.supportsLargeScreens = sForBoolean.unparcel(in);
+ this.supportsExtraLargeScreens = sForBoolean.unparcel(in);
+ this.resizeable = sForBoolean.unparcel(in);
+ this.anyDensity = sForBoolean.unparcel(in);
+ this.versionCode = in.readInt();
+ this.versionCodeMajor = in.readInt();
+ this.baseRevisionCode = in.readInt();
+ this.versionName = sForString.unparcel(in);
+ this.compileSdkVersion = in.readInt();
+ this.compileSdkVersionCodeName = sForString.unparcel(in);
+ this.packageName = sForString.unparcel(in);
+ this.realPackage = sForString.unparcel(in);
+ this.baseCodePath = sForString.unparcel(in);
+ this.requiredForAllUsers = in.readBoolean();
+ this.restrictedAccountType = sForString.unparcel(in);
+ this.requiredAccountType = sForString.unparcel(in);
+ this.overlayTarget = sForString.unparcel(in);
+ this.overlayTargetName = sForString.unparcel(in);
+ this.overlayCategory = sForString.unparcel(in);
+ this.overlayPriority = in.readInt();
+ this.overlayIsStatic = in.readBoolean();
+ this.overlayables = sForStringValueMap.unparcel(in);
+ this.staticSharedLibName = sForString.unparcel(in);
+ this.staticSharedLibVersion = in.readLong();
+ this.libraryNames = sForStringList.unparcel(in);
+ this.usesLibraries = sForStringList.unparcel(in);
+ this.usesOptionalLibraries = sForStringList.unparcel(in);
+ this.usesStaticLibraries = sForStringList.unparcel(in);
+ this.usesStaticLibrariesVersions = in.createLongArray();
+
+ int digestsSize = in.readInt();
+ if (digestsSize >= 0) {
+ this.usesStaticLibrariesCertDigests = new String[digestsSize][];
+ for (int index = 0; index < digestsSize; index++) {
+ this.usesStaticLibrariesCertDigests[index] = sForStringArray.unparcel(in);
+ }
+ }
+
+ this.sharedUserId = sForString.unparcel(in);
+ this.sharedUserLabel = in.readInt();
+ this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR);
+ this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR);
+ this.featureGroups = in.createTypedArrayList(FeatureGroupInfo.CREATOR);
+ this.restrictUpdateHash = in.createByteArray();
+ this.originalPackages = sForStringList.unparcel(in);
+ this.adoptPermissions = sForStringList.unparcel(in);
+ this.requestedPermissions = sForStringList.unparcel(in);
+ this.implicitPermissions = sForStringList.unparcel(in);
+ this.upgradeKeySets = sForStringSet.unparcel(in);
+ this.keySetMapping = in.readHashMap(boot);
+ this.protectedBroadcasts = sForStringList.unparcel(in);
+ this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
+ this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
+ this.services = in.createTypedArrayList(ParsedService.CREATOR);
+ this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
+ this.features = in.createTypedArrayList(ParsedFeature.CREATOR);
+ this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
+ this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
+ this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
+ this.preferredActivityFilters = sForIntentInfoPairs.unparcel(in);
+ this.processes = in.readHashMap(boot);
+ this.metaData = in.readBundle(boot);
+ this.volumeUuid = sForString.unparcel(in);
+ this.signingDetails = in.readParcelable(boot);
+ this.codePath = sForString.unparcel(in);
+ this.use32BitAbi = in.readBoolean();
+ this.visibleToInstantApps = in.readBoolean();
+ this.forceQueryable = in.readBoolean();
+ this.queriesIntents = in.createTypedArrayList(Intent.CREATOR);
+ this.queriesPackages = sForStringList.unparcel(in);
+ this.appComponentFactory = sForString.unparcel(in);
+ this.backupAgentName = sForString.unparcel(in);
+ this.banner = in.readInt();
+ this.category = in.readInt();
+ this.classLoaderName = sForString.unparcel(in);
+ this.className = sForString.unparcel(in);
+ this.compatibleWidthLimitDp = in.readInt();
+ this.descriptionRes = in.readInt();
+ this.enabled = in.readBoolean();
+ this.crossProfile = in.readBoolean();
+ this.fullBackupContent = in.readInt();
+ this.iconRes = in.readInt();
+ this.installLocation = in.readInt();
+ this.labelRes = in.readInt();
+ this.largestWidthLimitDp = in.readInt();
+ this.logo = in.readInt();
+ this.manageSpaceActivityName = sForString.unparcel(in);
+ this.maxAspectRatio = in.readFloat();
+ this.minAspectRatio = in.readFloat();
+ this.minSdkVersion = in.readInt();
+ this.networkSecurityConfigRes = in.readInt();
+ this.nonLocalizedLabel = in.readCharSequence();
+ this.permission = sForString.unparcel(in);
+ this.processName = sForString.unparcel(in);
+ this.requiresSmallestWidthDp = in.readInt();
+ this.roundIconRes = in.readInt();
+ this.targetSandboxVersion = in.readInt();
+ this.targetSdkVersion = in.readInt();
+ this.taskAffinity = sForString.unparcel(in);
+ this.theme = in.readInt();
+ this.uiOptions = in.readInt();
+ this.zygotePreloadName = sForString.unparcel(in);
+ this.splitClassLoaderNames = sForStringArray.unparcel(in);
+ this.splitCodePaths = sForStringArray.unparcel(in);
+ this.splitDependencies = in.readSparseArray(boot);
+ this.splitFlags = in.createIntArray();
+ this.splitNames = sForStringArray.unparcel(in);
+ this.splitRevisionCodes = in.createIntArray();
+ this.externalStorage = in.readBoolean();
+ this.baseHardwareAccelerated = in.readBoolean();
+ this.allowBackup = in.readBoolean();
+ this.killAfterRestore = in.readBoolean();
+ this.restoreAnyVersion = in.readBoolean();
+ this.fullBackupOnly = in.readBoolean();
+ this.persistent = in.readBoolean();
+ this.debuggable = in.readBoolean();
+ this.vmSafeMode = in.readBoolean();
+ this.hasCode = in.readBoolean();
+ this.allowTaskReparenting = in.readBoolean();
+ this.allowClearUserData = in.readBoolean();
+ this.largeHeap = in.readBoolean();
+ this.usesCleartextTraffic = in.readBoolean();
+ this.supportsRtl = in.readBoolean();
+ this.testOnly = in.readBoolean();
+ this.multiArch = in.readBoolean();
+ this.extractNativeLibs = in.readBoolean();
+ this.game = in.readBoolean();
+
+ this.resizeableActivity = sForBoolean.unparcel(in);
+
+ this.staticSharedLibrary = in.readBoolean();
+ this.overlay = in.readBoolean();
+ this.isolatedSplitLoading = in.readBoolean();
+ this.hasDomainUrls = in.readBoolean();
+ this.profileableByShell = in.readBoolean();
+ this.backupInForeground = in.readBoolean();
+ this.useEmbeddedDex = in.readBoolean();
+ this.defaultToDeviceProtectedStorage = in.readBoolean();
+ this.directBootAware = in.readBoolean();
+ this.partiallyDirectBootAware = in.readBoolean();
+ this.resizeableActivityViaSdkVersion = in.readBoolean();
+ this.allowClearUserDataOnFailedRestore = in.readBoolean();
+ this.allowAudioPlaybackCapture = in.readBoolean();
+ this.requestLegacyExternalStorage = in.readBoolean();
+ this.usesNonSdkApi = in.readBoolean();
+ this.hasFragileUserData = in.readBoolean();
+ this.cantSaveState = in.readBoolean();
+ this.allowNativeHeapPointerTagging = in.readBoolean();
+ this.preserveLegacyExternalStorage = in.readBoolean();
+ this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
+ }
+
+ public static final Parcelable.Creator<ParsingPackageImpl> CREATOR =
+ new Parcelable.Creator<ParsingPackageImpl>() {
+ @Override
+ public ParsingPackageImpl createFromParcel(Parcel source) {
+ return new ParsingPackageImpl(source);
+ }
+
+ @Override
+ public ParsingPackageImpl[] newArray(int size) {
+ return new ParsingPackageImpl[size];
+ }
+ };
+
+ @Override
+ public int getVersionCode() {
+ return versionCode;
+ }
+
+ @Override
+ public int getVersionCodeMajor() {
+ return versionCodeMajor;
+ }
+
+ @Override
+ public int getBaseRevisionCode() {
+ return baseRevisionCode;
+ }
+
+ @Nullable
+ @Override
+ public String getVersionName() {
+ return versionName;
+ }
+
+ @Override
+ public int getCompileSdkVersion() {
+ return compileSdkVersion;
+ }
+
+ @Nullable
+ @Override
+ public String getCompileSdkVersionCodeName() {
+ return compileSdkVersionCodeName;
+ }
+
+ @NonNull
+ @Override
+ public String getPackageName() {
+ return packageName;
+ }
+
+ @Nullable
+ @Override
+ public String getRealPackage() {
+ return realPackage;
+ }
+
+ @NonNull
+ @Override
+ public String getBaseCodePath() {
+ return baseCodePath;
+ }
+
+ @Override
+ public boolean isRequiredForAllUsers() {
+ return requiredForAllUsers;
+ }
+
+ @Nullable
+ @Override
+ public String getRestrictedAccountType() {
+ return restrictedAccountType;
+ }
+
+ @Nullable
+ @Override
+ public String getRequiredAccountType() {
+ return requiredAccountType;
+ }
+
+ @Nullable
+ @Override
+ public String getOverlayTarget() {
+ return overlayTarget;
+ }
+
+ @Nullable
+ @Override
+ public String getOverlayTargetName() {
+ return overlayTargetName;
+ }
+
+ @Nullable
+ @Override
+ public String getOverlayCategory() {
+ return overlayCategory;
+ }
+
+ @Override
+ public int getOverlayPriority() {
+ return overlayPriority;
+ }
+
+ @Override
+ public boolean isOverlayIsStatic() {
+ return overlayIsStatic;
+ }
+
+ @NonNull
+ @Override
+ public Map<String,String> getOverlayables() {
+ return overlayables;
+ }
+
+ @Nullable
+ @Override
+ public String getStaticSharedLibName() {
+ return staticSharedLibName;
+ }
+
+ @Override
+ public long getStaticSharedLibVersion() {
+ return staticSharedLibVersion;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getLibraryNames() {
+ return libraryNames;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesLibraries() {
+ return usesLibraries;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesOptionalLibraries() {
+ return usesOptionalLibraries;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesStaticLibraries() {
+ return usesStaticLibraries;
+ }
+
+ @Nullable
+ @Override
+ public long[] getUsesStaticLibrariesVersions() {
+ return usesStaticLibrariesVersions;
+ }
+
+ @Nullable
+ @Override
+ public String[][] getUsesStaticLibrariesCertDigests() {
+ return usesStaticLibrariesCertDigests;
+ }
+
+ @Nullable
+ @Override
+ public String getSharedUserId() {
+ return sharedUserId;
+ }
+
+ @Override
+ public int getSharedUserLabel() {
+ return sharedUserLabel;
+ }
+
+ @NonNull
+ @Override
+ public List<ConfigurationInfo> getConfigPreferences() {
+ return configPreferences;
+ }
+
+ @NonNull
+ @Override
+ public List<FeatureInfo> getReqFeatures() {
+ return reqFeatures;
+ }
+
+ @NonNull
+ @Override
+ public List<FeatureGroupInfo> getFeatureGroups() {
+ return featureGroups;
+ }
+
+ @Nullable
+ @Override
+ public byte[] getRestrictUpdateHash() {
+ return restrictUpdateHash;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getOriginalPackages() {
+ return originalPackages;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getAdoptPermissions() {
+ return adoptPermissions;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getRequestedPermissions() {
+ return requestedPermissions;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getImplicitPermissions() {
+ return implicitPermissions;
+ }
+
+ @NonNull
+ @Override
+ public Set<String> getUpgradeKeySets() {
+ return upgradeKeySets;
+ }
+
+ @NonNull
+ @Override
+ public Map<String,ArraySet<PublicKey>> getKeySetMapping() {
+ return keySetMapping;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getProtectedBroadcasts() {
+ return protectedBroadcasts;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedActivity> getActivities() {
+ return activities;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedActivity> getReceivers() {
+ return receivers;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedService> getServices() {
+ return services;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedProvider> getProviders() {
+ return providers;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedFeature> getFeatures() {
+ return features;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedPermission> getPermissions() {
+ return permissions;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedPermissionGroup> getPermissionGroups() {
+ return permissionGroups;
+ }
+
+ @NonNull
+ @Override
+ public List<ParsedInstrumentation> getInstrumentations() {
+ return instrumentations;
+ }
+
+ @NonNull
+ @Override
+ public List<Pair<String,ParsedIntentInfo>> getPreferredActivityFilters() {
+ return preferredActivityFilters;
+ }
+
+ @NonNull
+ @Override
+ public Map<String,ParsedProcess> getProcesses() {
+ return processes;
+ }
+
+ @Nullable
+ @Override
+ public Bundle getMetaData() {
+ return metaData;
+ }
+
+ private void addMimeGroupsFromComponent(ParsedComponent component) {
+ for (int i = component.getIntents().size() - 1; i >= 0; i--) {
+ IntentFilter filter = component.getIntents().get(i);
+ for (int groupIndex = filter.countMimeGroups() - 1; groupIndex >= 0; groupIndex--) {
+ mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
+ }
+ }
+ }
+
+ @Override
+ @Nullable
+ public Set<String> getMimeGroups() {
+ return mimeGroups;
+ }
+
+ @Nullable
+ @Override
+ public String getVolumeUuid() {
+ return volumeUuid;
+ }
+
+ @Nullable
+ @Override
+ public PackageParser.SigningDetails getSigningDetails() {
+ return signingDetails;
+ }
+
+ @NonNull
+ @Override
+ public String getCodePath() {
+ return codePath;
+ }
+
+ @Override
+ public boolean isUse32BitAbi() {
+ return use32BitAbi;
+ }
+
+ @Override
+ public boolean isVisibleToInstantApps() {
+ return visibleToInstantApps;
+ }
+
+ @Override
+ public boolean isForceQueryable() {
+ return forceQueryable;
+ }
+
+ @NonNull
+ @Override
+ public List<Intent> getQueriesIntents() {
+ return queriesIntents;
+ }
+
+ @NonNull
+ @Override
+ public List<String> getQueriesPackages() {
+ return queriesPackages;
+ }
+
+ @NonNull
+ @Override
+ public Set<String> getQueriesProviders() {
+ return queriesProviders;
+ }
+
+ @Nullable
+ @Override
+ public String[] getSplitClassLoaderNames() {
+ return splitClassLoaderNames;
+ }
+
+ @Nullable
+ @Override
+ public String[] getSplitCodePaths() {
+ return splitCodePaths;
+ }
+
+ @Nullable
+ @Override
+ public SparseArray<int[]> getSplitDependencies() {
+ return splitDependencies;
+ }
+
+ @Nullable
+ @Override
+ public int[] getSplitFlags() {
+ return splitFlags;
+ }
+
+ @Nullable
+ @Override
+ public String[] getSplitNames() {
+ return splitNames;
+ }
+
+ @Nullable
+ @Override
+ public int[] getSplitRevisionCodes() {
+ return splitRevisionCodes;
+ }
+
+ @Nullable
+ @Override
+ public String getAppComponentFactory() {
+ return appComponentFactory;
+ }
+
+ @Nullable
+ @Override
+ public String getBackupAgentName() {
+ return backupAgentName;
+ }
+
+ @Override
+ public int getBanner() {
+ return banner;
+ }
+
+ @Override
+ public int getCategory() {
+ return category;
+ }
+
+ @Nullable
+ @Override
+ public String getClassLoaderName() {
+ return classLoaderName;
+ }
+
+ @Nullable
+ @Override
+ public String getClassName() {
+ return className;
+ }
+
+ @Override
+ public int getCompatibleWidthLimitDp() {
+ return compatibleWidthLimitDp;
+ }
+
+ @Override
+ public int getDescriptionRes() {
+ return descriptionRes;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public boolean isCrossProfile() {
+ return crossProfile;
+ }
+
+ @Override
+ public int getFullBackupContent() {
+ return fullBackupContent;
+ }
+
+ @Override
+ public int getIconRes() {
+ return iconRes;
+ }
+
+ @Override
+ public int getInstallLocation() {
+ return installLocation;
+ }
+
+ @Override
+ public int getLabelRes() {
+ return labelRes;
+ }
+
+ @Override
+ public int getLargestWidthLimitDp() {
+ return largestWidthLimitDp;
+ }
+
+ @Override
+ public int getLogo() {
+ return logo;
+ }
+
+ @Nullable
+ @Override
+ public String getManageSpaceActivityName() {
+ return manageSpaceActivityName;
+ }
+
+ @Override
+ public float getMaxAspectRatio() {
+ return maxAspectRatio;
+ }
+
+ @Override
+ public float getMinAspectRatio() {
+ return minAspectRatio;
+ }
+
+ @Override
+ public int getMinSdkVersion() {
+ return minSdkVersion;
+ }
+
+ @Override
+ public int getNetworkSecurityConfigRes() {
+ return networkSecurityConfigRes;
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getNonLocalizedLabel() {
+ return nonLocalizedLabel;
+ }
+
+ @Nullable
+ @Override
+ public String getPermission() {
+ return permission;
+ }
+
+ @Override
+ public int getRequiresSmallestWidthDp() {
+ return requiresSmallestWidthDp;
+ }
+
+ @Override
+ public int getRoundIconRes() {
+ return roundIconRes;
+ }
+
+ @Override
+ public int getTargetSandboxVersion() {
+ return targetSandboxVersion;
+ }
+
+ @Override
+ public int getTargetSdkVersion() {
+ return targetSdkVersion;
+ }
+
+ @Nullable
+ @Override
+ public String getTaskAffinity() {
+ return taskAffinity;
+ }
+
+ @Override
+ public int getTheme() {
+ return theme;
+ }
+
+ @Override
+ public int getUiOptions() {
+ return uiOptions;
+ }
+
+ @Nullable
+ @Override
+ public String getZygotePreloadName() {
+ return zygotePreloadName;
+ }
+
+ @Override
+ public boolean isExternalStorage() {
+ return externalStorage;
+ }
+
+ @Override
+ public boolean isBaseHardwareAccelerated() {
+ return baseHardwareAccelerated;
+ }
+
+ @Override
+ public boolean isAllowBackup() {
+ return allowBackup;
+ }
+
+ @Override
+ public boolean isKillAfterRestore() {
+ return killAfterRestore;
+ }
+
+ @Override
+ public boolean isRestoreAnyVersion() {
+ return restoreAnyVersion;
+ }
+
+ @Override
+ public boolean isFullBackupOnly() {
+ return fullBackupOnly;
+ }
+
+ @Override
+ public boolean isPersistent() {
+ return persistent;
+ }
+
+ @Override
+ public boolean isDebuggable() {
+ return debuggable;
+ }
+
+ @Override
+ public boolean isVmSafeMode() {
+ return vmSafeMode;
+ }
+
+ @Override
+ public boolean isHasCode() {
+ return hasCode;
+ }
+
+ @Override
+ public boolean isAllowTaskReparenting() {
+ return allowTaskReparenting;
+ }
+
+ @Override
+ public boolean isAllowClearUserData() {
+ return allowClearUserData;
+ }
+
+ @Override
+ public boolean isLargeHeap() {
+ return largeHeap;
+ }
+
+ @Override
+ public boolean isUsesCleartextTraffic() {
+ return usesCleartextTraffic;
+ }
+
+ @Override
+ public boolean isSupportsRtl() {
+ return supportsRtl;
+ }
+
+ @Override
+ public boolean isTestOnly() {
+ return testOnly;
+ }
+
+ @Override
+ public boolean isMultiArch() {
+ return multiArch;
+ }
+
+ @Override
+ public boolean isExtractNativeLibs() {
+ return extractNativeLibs;
+ }
+
+ @Override
+ public boolean isGame() {
+ return game;
+ }
+
+ /**
+ * @see ParsingPackageRead#getResizeableActivity()
+ */
+ @Nullable
+ @Override
+ public Boolean getResizeableActivity() {
+ return resizeableActivity;
+ }
+
+ @Override
+ public boolean isStaticSharedLibrary() {
+ return staticSharedLibrary;
+ }
+
+ @Override
+ public boolean isOverlay() {
+ return overlay;
+ }
+
+ @Override
+ public boolean isIsolatedSplitLoading() {
+ return isolatedSplitLoading;
+ }
+
+ @Override
+ public boolean isHasDomainUrls() {
+ return hasDomainUrls;
+ }
+
+ @Override
+ public boolean isProfileableByShell() {
+ return profileableByShell;
+ }
+
+ @Override
+ public boolean isBackupInForeground() {
+ return backupInForeground;
+ }
+
+ @Override
+ public boolean isUseEmbeddedDex() {
+ return useEmbeddedDex;
+ }
+
+ @Override
+ public boolean isDefaultToDeviceProtectedStorage() {
+ return defaultToDeviceProtectedStorage;
+ }
+
+ @Override
+ public boolean isDirectBootAware() {
+ return directBootAware;
+ }
+
+ @Override
+ public boolean isPartiallyDirectBootAware() {
+ return partiallyDirectBootAware;
+ }
+
+ @Override
+ public boolean isResizeableActivityViaSdkVersion() {
+ return resizeableActivityViaSdkVersion;
+ }
+
+ @Override
+ public boolean isAllowClearUserDataOnFailedRestore() {
+ return allowClearUserDataOnFailedRestore;
+ }
+
+ @Override
+ public boolean isAllowAudioPlaybackCapture() {
+ return allowAudioPlaybackCapture;
+ }
+
+ @Override
+ public boolean isRequestLegacyExternalStorage() {
+ return requestLegacyExternalStorage;
+ }
+
+ @Override
+ public boolean isUsesNonSdkApi() {
+ return usesNonSdkApi;
+ }
+
+ @Override
+ public boolean isHasFragileUserData() {
+ return hasFragileUserData;
+ }
+
+ @Override
+ public boolean isCantSaveState() {
+ return cantSaveState;
+ }
+
+ @Override
+ public boolean isAllowNativeHeapPointerTagging() {
+ return allowNativeHeapPointerTagging;
+ }
+
+ @Override
+ public boolean hasPreserveLegacyExternalStorage() {
+ return preserveLegacyExternalStorage;
+ }
+
+ @Override
+ public ParsingPackageImpl setBaseRevisionCode(int value) {
+ baseRevisionCode = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setCompileSdkVersion(int value) {
+ compileSdkVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRequiredForAllUsers(boolean value) {
+ requiredForAllUsers = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setOverlayPriority(int value) {
+ overlayPriority = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setOverlayIsStatic(boolean value) {
+ overlayIsStatic = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setStaticSharedLibVersion(long value) {
+ staticSharedLibVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSharedUserLabel(int value) {
+ sharedUserLabel = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRestrictUpdateHash(@Nullable byte... value) {
+ restrictUpdateHash = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setUpgradeKeySets(@NonNull Set<String> value) {
+ upgradeKeySets = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setProcesses(@NonNull Map<String,ParsedProcess> value) {
+ processes = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setMetaData(@Nullable Bundle value) {
+ metaData = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ signingDetails = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setUse32BitAbi(boolean value) {
+ use32BitAbi = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setVisibleToInstantApps(boolean value) {
+ visibleToInstantApps = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setForceQueryable(boolean value) {
+ forceQueryable = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setBanner(int value) {
+ banner = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setCategory(int value) {
+ category = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setCompatibleWidthLimitDp(int value) {
+ compatibleWidthLimitDp = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setDescriptionRes(int value) {
+ descriptionRes = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setEnabled(boolean value) {
+ enabled = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setCrossProfile(boolean value) {
+ crossProfile = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setFullBackupContent(int value) {
+ fullBackupContent = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setIconRes(int value) {
+ iconRes = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setInstallLocation(int value) {
+ installLocation = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setLabelRes(int value) {
+ labelRes = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setLargestWidthLimitDp(int value) {
+ largestWidthLimitDp = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setLogo(int value) {
+ logo = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setMaxAspectRatio(float value) {
+ maxAspectRatio = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setMinAspectRatio(float value) {
+ minAspectRatio = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setMinSdkVersion(int value) {
+ minSdkVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setNetworkSecurityConfigRes(int value) {
+ networkSecurityConfigRes = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setNonLocalizedLabel(@Nullable CharSequence value) {
+ nonLocalizedLabel = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRequiresSmallestWidthDp(int value) {
+ requiresSmallestWidthDp = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRoundIconRes(int value) {
+ roundIconRes = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setTargetSandboxVersion(int value) {
+ targetSandboxVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setTargetSdkVersion(int value) {
+ targetSdkVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setTheme(int value) {
+ theme = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setUiOptions(int value) {
+ uiOptions = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setExternalStorage(boolean value) {
+ externalStorage = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setBaseHardwareAccelerated(boolean value) {
+ baseHardwareAccelerated = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowBackup(boolean value) {
+ allowBackup = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setKillAfterRestore(boolean value) {
+ killAfterRestore = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRestoreAnyVersion(boolean value) {
+ restoreAnyVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setFullBackupOnly(boolean value) {
+ fullBackupOnly = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setPersistent(boolean value) {
+ persistent = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setDebuggable(boolean value) {
+ debuggable = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setVmSafeMode(boolean value) {
+ vmSafeMode = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setHasCode(boolean value) {
+ hasCode = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowTaskReparenting(boolean value) {
+ allowTaskReparenting = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowClearUserData(boolean value) {
+ allowClearUserData = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setLargeHeap(boolean value) {
+ largeHeap = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setUsesCleartextTraffic(boolean value) {
+ usesCleartextTraffic = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSupportsRtl(boolean value) {
+ supportsRtl = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setTestOnly(boolean value) {
+ testOnly = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setMultiArch(boolean value) {
+ multiArch = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setExtractNativeLibs(boolean value) {
+ extractNativeLibs = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setGame(boolean value) {
+ game = value;
+ return this;
+ }
+
+ /**
+ * @see ParsingPackageRead#getResizeableActivity()
+ */
+ @Override
+ public ParsingPackageImpl setResizeableActivity(@Nullable Boolean value) {
+ resizeableActivity = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
+ staticSharedLibrary = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setOverlay(boolean value) {
+ overlay = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setIsolatedSplitLoading(boolean value) {
+ isolatedSplitLoading = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setHasDomainUrls(boolean value) {
+ hasDomainUrls = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setProfileableByShell(boolean value) {
+ profileableByShell = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setBackupInForeground(boolean value) {
+ backupInForeground = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setUseEmbeddedDex(boolean value) {
+ useEmbeddedDex = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setDefaultToDeviceProtectedStorage(boolean value) {
+ defaultToDeviceProtectedStorage = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setDirectBootAware(boolean value) {
+ directBootAware = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setPartiallyDirectBootAware(boolean value) {
+ partiallyDirectBootAware = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setResizeableActivityViaSdkVersion(boolean value) {
+ resizeableActivityViaSdkVersion = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowClearUserDataOnFailedRestore(boolean value) {
+ allowClearUserDataOnFailedRestore = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowAudioPlaybackCapture(boolean value) {
+ allowAudioPlaybackCapture = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setRequestLegacyExternalStorage(boolean value) {
+ requestLegacyExternalStorage = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setUsesNonSdkApi(boolean value) {
+ usesNonSdkApi = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setHasFragileUserData(boolean value) {
+ hasFragileUserData = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setCantSaveState(boolean value) {
+ cantSaveState = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setAllowNativeHeapPointerTagging(boolean value) {
+ allowNativeHeapPointerTagging = value;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setPreserveLegacyExternalStorage(boolean value) {
+ preserveLegacyExternalStorage = value;
+ return this;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
new file mode 100644
index 0000000..048b924
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -0,0 +1,845 @@
+/*
+ * Copyright (C) 2020 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.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Everything written by {@link ParsingPackage} and readable back.
+ *
+ * @hide
+ */
+@SuppressWarnings("UnusedReturnValue")
+public interface ParsingPackageRead extends Parcelable {
+
+ /**
+ * @see ActivityInfo
+ * @see PackageInfo#activities
+ */
+ @NonNull
+ List<ParsedActivity> getActivities();
+
+ /**
+ * The names of packages to adopt ownership of permissions from, parsed under
+ * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+ * @see R.styleable#AndroidManifestOriginalPackage_name
+ */
+ @NonNull
+ List<String> getAdoptPermissions();
+
+ /**
+ * @see PackageInfo#configPreferences
+ * @see R.styleable#AndroidManifestUsesConfiguration
+ */
+ @NonNull
+ List<ConfigurationInfo> getConfigPreferences();
+
+ @NonNull
+ List<ParsedFeature> getFeatures();
+
+ /**
+ * @see PackageInfo#featureGroups
+ * @see R.styleable#AndroidManifestUsesFeature
+ */
+ @NonNull
+ List<FeatureGroupInfo> getFeatureGroups();
+
+ /**
+ * Permissions requested but not in the manifest. These may have been split or migrated from
+ * previous versions/definitions.
+ */
+ @NonNull
+ List<String> getImplicitPermissions();
+
+ /**
+ * @see android.content.pm.InstrumentationInfo
+ * @see PackageInfo#instrumentation
+ */
+ @NonNull
+ List<ParsedInstrumentation> getInstrumentations();
+
+ /**
+ * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
+ * {@link PackageParser#TAG_KEY_SETS}.
+ * @see R.styleable#AndroidManifestKeySet
+ * @see R.styleable#AndroidManifestPublicKey
+ */
+ @NonNull
+ Map<String, ArraySet<PublicKey>> getKeySetMapping();
+
+ /**
+ * Library names this package is declared as, for use by other packages with "uses-library".
+ * @see R.styleable#AndroidManifestLibrary
+ */
+ @NonNull
+ List<String> getLibraryNames();
+
+ /**
+ * For system use to migrate from an old package name to a new one, moving over data
+ * if available.
+ * @see R.styleable#AndroidManifestOriginalPackage}
+ */
+ @NonNull
+ List<String> getOriginalPackages();
+
+ /**
+ * Map of overlayable name to actor name.
+ */
+ @NonNull
+ Map<String, String> getOverlayables();
+
+ /**
+ * @see android.content.pm.PermissionInfo
+ * @see PackageInfo#permissions
+ */
+ @NonNull
+ List<ParsedPermission> getPermissions();
+
+ /**
+ * @see android.content.pm.PermissionGroupInfo
+ */
+ @NonNull
+ List<ParsedPermissionGroup> getPermissionGroups();
+
+ /**
+ * Used to determine the default preferred handler of an {@link Intent}.
+ *
+ * Map of component className to intent info inside that component.
+ * TODO(b/135203078): Is this actually used/working?
+ */
+ @NonNull
+ List<Pair<String, ParsedIntentInfo>> getPreferredActivityFilters();
+
+ /**
+ * System protected broadcasts.
+ * @see R.styleable#AndroidManifestProtectedBroadcast
+ */
+ @NonNull
+ List<String> getProtectedBroadcasts();
+
+ /**
+ * @see android.content.pm.ProviderInfo
+ * @see PackageInfo#providers
+ */
+ @NonNull
+ List<ParsedProvider> getProviders();
+
+ /**
+ * @see android.content.pm.ProcessInfo
+ */
+ @NonNull
+ Map<String, ParsedProcess> getProcesses();
+
+ /**
+ * Since they share several attributes, receivers are parsed as {@link ParsedActivity}, even
+ * though they represent different functionality.
+ * TODO(b/135203078): Reconsider this and maybe make ParsedReceiver so it's not so confusing
+ * @see ActivityInfo
+ * @see PackageInfo#receivers
+ */
+ @NonNull
+ List<ParsedActivity> getReceivers();
+
+ /**
+ * @see PackageInfo#reqFeatures
+ * @see R.styleable#AndroidManifestUsesFeature
+ */
+ @NonNull
+ List<FeatureInfo> getReqFeatures();
+
+ /**
+ * All the permissions declared. This is an effective set, and may include permissions
+ * transformed from split/migrated permissions from previous versions, so may not be exactly
+ * what the package declares in its manifest.
+ * @see PackageInfo#requestedPermissions
+ * @see R.styleable#AndroidManifestUsesPermission
+ */
+ @NonNull
+ List<String> getRequestedPermissions();
+
+ /**
+ * Whether or not the app requested explicitly resizeable Activities.
+ * A null value means nothing was explicitly requested.
+ */
+ @Nullable
+ Boolean getResizeableActivity();
+
+ /**
+ * @see ServiceInfo
+ * @see PackageInfo#services
+ */
+ @NonNull
+ List<ParsedService> getServices();
+
+ /** @see R.styleable#AndroidManifestUsesLibrary */
+ @NonNull
+ List<String> getUsesLibraries();
+
+ /**
+ * Like {@link #getUsesLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesLibrary_required} to false . Application is expected
+ * to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalLibraries();
+
+ /**
+ * TODO(b/135203078): Move static library stuff to an inner data class
+ * @see R.styleable#AndroidManifestUsesStaticLibrary
+ */
+ @NonNull
+ List<String> getUsesStaticLibraries();
+
+ /** @see R.styleable#AndroidManifestUsesStaticLibrary_certDigest */
+ @Nullable
+ String[][] getUsesStaticLibrariesCertDigests();
+
+ /** @see R.styleable#AndroidManifestUsesStaticLibrary_version */
+ @Nullable
+ long[] getUsesStaticLibrariesVersions();
+
+ /**
+ * Intents that this package may query or require and thus requires visibility into.
+ * @see R.styleable#AndroidManifestQueriesIntent
+ */
+ @NonNull
+ List<Intent> getQueriesIntents();
+
+ /**
+ * Other packages that this package may query or require and thus requires visibility into.
+ * @see R.styleable#AndroidManifestQueriesPackage
+ */
+ @NonNull
+ List<String> getQueriesPackages();
+
+ /**
+ * Authorities that this package may query or require and thus requires visibility into.
+ * @see R.styleable#AndroidManifestQueriesProvider
+ */
+ @NonNull
+ Set<String> getQueriesProviders();
+
+ /**
+ * We store the application meta-data independently to avoid multiple unwanted references
+ * TODO(b/135203078): What does this comment mean?
+ * TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
+ */
+ @Nullable
+ Bundle getMetaData();
+
+ /** @see R.styleable#AndroidManifestApplication_forceQueryable */
+ boolean isForceQueryable();
+
+ /**
+ * @see ApplicationInfo#maxAspectRatio
+ * @see R.styleable#AndroidManifestApplication_maxAspectRatio
+ */
+ float getMaxAspectRatio();
+
+ /**
+ * @see ApplicationInfo#minAspectRatio
+ * @see R.styleable#AndroidManifestApplication_minAspectRatio
+ */
+ float getMinAspectRatio();
+
+ /**
+ * @see ApplicationInfo#permission
+ * @see R.styleable#AndroidManifestApplication_permission
+ */
+ @Nullable
+ String getPermission();
+
+ /**
+ * @see ApplicationInfo#processName
+ * @see R.styleable#AndroidManifestApplication_process
+ */
+ @NonNull
+ String getProcessName();
+
+ /**
+ * @see PackageInfo#sharedUserId
+ * @see R.styleable#AndroidManifest_sharedUserId
+ */
+ @Deprecated
+ @Nullable
+ String getSharedUserId();
+
+ /** @see R.styleable#AndroidManifestStaticLibrary_name */
+ @Nullable
+ String getStaticSharedLibName();
+
+ /**
+ * @see ApplicationInfo#taskAffinity
+ * @see R.styleable#AndroidManifestApplication_taskAffinity
+ */
+ @Nullable
+ String getTaskAffinity();
+
+ /**
+ * @see ApplicationInfo#targetSdkVersion
+ * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
+ */
+ int getTargetSdkVersion();
+
+ /**
+ * @see ApplicationInfo#uiOptions
+ * @see R.styleable#AndroidManifestApplication_uiOptions
+ */
+ int getUiOptions();
+
+ boolean isCrossProfile();
+
+ boolean isResizeableActivityViaSdkVersion();
+
+ /** @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED */
+ boolean isBaseHardwareAccelerated();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_resizeable
+ * @see ApplicationInfo#FLAG_RESIZEABLE_FOR_SCREENS
+ */
+ boolean isResizeable();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE */
+ boolean isAllowAudioPlaybackCapture();
+
+ /** @see ApplicationInfo#FLAG_ALLOW_BACKUP */
+ boolean isAllowBackup();
+
+ /** @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA */
+ boolean isAllowClearUserData();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE */
+ boolean isAllowClearUserDataOnFailedRestore();
+
+ /** @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING */
+ boolean isAllowTaskReparenting();
+
+ /**
+ * @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
+ * @see ApplicationInfo#isResourceOverlay()
+ */
+ boolean isOverlay();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND */
+ boolean isBackupInForeground();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE */
+ boolean isCantSaveState();
+
+ /** @see ApplicationInfo#FLAG_DEBUGGABLE */
+ boolean isDebuggable();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE */
+ boolean isDefaultToDeviceProtectedStorage();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE */
+ boolean isDirectBootAware();
+
+ /** @see ApplicationInfo#FLAG_EXTERNAL_STORAGE */
+ boolean isExternalStorage();
+
+ /** @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS */
+ boolean isExtractNativeLibs();
+
+ /** @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY */
+ boolean isFullBackupOnly();
+
+ /** @see ApplicationInfo#FLAG_HAS_CODE */
+ boolean isHasCode();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA */
+ boolean isHasFragileUserData();
+
+ /** @see ApplicationInfo#FLAG_IS_GAME */
+ @Deprecated
+ boolean isGame();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING */
+ boolean isIsolatedSplitLoading();
+
+ /** @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE */
+ boolean isKillAfterRestore();
+
+ /** @see ApplicationInfo#FLAG_LARGE_HEAP */
+ boolean isLargeHeap();
+
+ /** @see ApplicationInfo#FLAG_MULTIARCH */
+ boolean isMultiArch();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE */
+ boolean isPartiallyDirectBootAware();
+
+ /** @see ApplicationInfo#FLAG_PERSISTENT */
+ boolean isPersistent();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL */
+ boolean isProfileableByShell();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE */
+ boolean isRequestLegacyExternalStorage();
+
+ /** @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION */
+ boolean isRestoreAnyVersion();
+
+ // ParsingPackageRead setSplitHasCode(int splitIndex, boolean splitHasCode);
+
+ /** Flags of any split APKs; ordered by parsed splitName */
+ @Nullable
+ int[] getSplitFlags();
+
+ /** @see ApplicationInfo#splitSourceDirs */
+ @Nullable
+ String[] getSplitCodePaths();
+
+ /** @see ApplicationInfo#splitDependencies */
+ @Nullable
+ SparseArray<int[]> getSplitDependencies();
+
+ /**
+ * @see ApplicationInfo#splitNames
+ * @see PackageInfo#splitNames
+ */
+ @Nullable
+ String[] getSplitNames();
+
+ /** @see PackageInfo#splitRevisionCodes */
+ int[] getSplitRevisionCodes();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY */
+ boolean isStaticSharedLibrary();
+
+ /** @see ApplicationInfo#FLAG_SUPPORTS_RTL */
+ boolean isSupportsRtl();
+
+ /** @see ApplicationInfo#FLAG_TEST_ONLY */
+ boolean isTestOnly();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX */
+ boolean isUseEmbeddedDex();
+
+ /** @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC */
+ boolean isUsesCleartextTraffic();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API */
+ boolean isUsesNonSdkApi();
+
+ /**
+ * Set if the any of components are visible to instant applications.
+ * @see R.styleable#AndroidManifestActivity_visibleToInstantApps
+ * @see R.styleable#AndroidManifestProvider_visibleToInstantApps
+ * @see R.styleable#AndroidManifestService_visibleToInstantApps
+ */
+ boolean isVisibleToInstantApps();
+
+ /** @see ApplicationInfo#FLAG_VM_SAFE_MODE */
+ boolean isVmSafeMode();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_anyDensity
+ * @see ApplicationInfo#FLAG_SUPPORTS_SCREEN_DENSITIES
+ */
+ boolean isAnyDensity();
+
+ /**
+ * @see ApplicationInfo#appComponentFactory
+ * @see R.styleable#AndroidManifestApplication_appComponentFactory
+ */
+ @Nullable
+ String getAppComponentFactory();
+
+ /**
+ * @see ApplicationInfo#backupAgentName
+ * @see R.styleable#AndroidManifestApplication_backupAgent
+ */
+ @Nullable
+ String getBackupAgentName();
+
+ /**
+ * @see ApplicationInfo#banner
+ * @see R.styleable#AndroidManifestApplication_banner
+ */
+ int getBanner();
+
+ /**
+ * @see ApplicationInfo#category
+ * @see R.styleable#AndroidManifestApplication_appCategory
+ */
+ int getCategory();
+
+ /**
+ * @see ApplicationInfo#classLoaderName
+ * @see R.styleable#AndroidManifestApplication_classLoader
+ */
+ @Nullable
+ String getClassLoaderName();
+
+ /**
+ * @see ApplicationInfo#className
+ * @see R.styleable#AndroidManifestApplication_name
+ */
+ @Nullable
+ String getClassName();
+
+ String getPackageName();
+
+ /** Path of base APK */
+ String getBaseCodePath();
+
+ /**
+ * Path where this package was found on disk. For monolithic packages
+ * this is path to single base APK file; for cluster packages this is
+ * path to the cluster directory.
+ */
+ @NonNull
+ String getCodePath();
+
+ /**
+ * @see ApplicationInfo#compatibleWidthLimitDp
+ * @see R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
+ */
+ int getCompatibleWidthLimitDp();
+
+ /**
+ * @see ApplicationInfo#descriptionRes
+ * @see R.styleable#AndroidManifestApplication_description
+ */
+ int getDescriptionRes();
+
+ /**
+ * @see ApplicationInfo#enabled
+ * @see R.styleable#AndroidManifestApplication_enabled
+ */
+ boolean isEnabled();
+
+ /**
+ * @see ApplicationInfo#fullBackupContent
+ * @see R.styleable#AndroidManifestApplication_fullBackupContent
+ */
+ int getFullBackupContent();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS */
+ boolean isHasDomainUrls();
+
+ /**
+ * @see ApplicationInfo#iconRes
+ * @see R.styleable#AndroidManifestApplication_icon
+ */
+ int getIconRes();
+
+ /**
+ * @see ApplicationInfo#installLocation
+ * @see R.styleable#AndroidManifest_installLocation
+ */
+ int getInstallLocation();
+
+ /**
+ * @see ApplicationInfo#labelRes
+ * @see R.styleable#AndroidManifestApplication_label
+ */
+ int getLabelRes();
+
+ /**
+ * @see ApplicationInfo#largestWidthLimitDp
+ * @see R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
+ */
+ int getLargestWidthLimitDp();
+
+ /**
+ * @see ApplicationInfo#logo
+ * @see R.styleable#AndroidManifestApplication_logo
+ */
+ int getLogo();
+
+ /**
+ * @see ApplicationInfo#manageSpaceActivityName
+ * @see R.styleable#AndroidManifestApplication_manageSpaceActivity
+ */
+ @Nullable
+ String getManageSpaceActivityName();
+
+ /**
+ * @see ApplicationInfo#minSdkVersion
+ * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
+ */
+ int getMinSdkVersion();
+
+ /**
+ * @see ApplicationInfo#networkSecurityConfigRes
+ * @see R.styleable#AndroidManifestApplication_networkSecurityConfig
+ */
+ int getNetworkSecurityConfigRes();
+
+ /**
+ * If {@link R.styleable#AndroidManifestApplication_label} is a string literal, this is it.
+ * Otherwise, it's stored as {@link #getLabelRes()}.
+ * @see ApplicationInfo#nonLocalizedLabel
+ * @see R.styleable#AndroidManifestApplication_label
+ */
+ @Nullable
+ CharSequence getNonLocalizedLabel();
+
+ /**
+ * @see PackageInfo#overlayCategory
+ * @see R.styleable#AndroidManifestResourceOverlay_category
+ */
+ @Nullable
+ String getOverlayCategory();
+
+ /** @see PackageInfo#mOverlayIsStatic */
+ boolean isOverlayIsStatic();
+
+ /**
+ * @see PackageInfo#overlayPriority
+ * @see R.styleable#AndroidManifestResourceOverlay_priority
+ */
+ int getOverlayPriority();
+
+ /**
+ * @see PackageInfo#overlayTarget
+ * @see R.styleable#AndroidManifestResourceOverlay_targetPackage
+ */
+ @Nullable
+ String getOverlayTarget();
+
+ /**
+ * @see PackageInfo#targetOverlayableName
+ * @see R.styleable#AndroidManifestResourceOverlay_targetName
+ */
+ @Nullable
+ String getOverlayTargetName();
+
+ /**
+ * If a system app declares {@link #getOriginalPackages()}, and the app was previously installed
+ * under one of those original package names, the {@link #getPackageName()} system identifier
+ * will be changed to that previously installed name. This will then be non-null, set to the
+ * manifest package name, for tracking the package under its true name.
+ *
+ * TODO(b/135203078): Remove this in favor of checking originalPackages.isEmpty and
+ * getManifestPackageName
+ */
+ @Nullable
+ String getRealPackage();
+
+ /**
+ * The required account type without which this application will not function.
+ *
+ * @see PackageInfo#requiredAccountType
+ * @see R.styleable#AndroidManifestApplication_requiredAccountType
+ */
+ @Nullable
+ String getRequiredAccountType();
+
+ /**
+ * @see PackageInfo#requiredForAllUsers
+ * @see R.styleable#AndroidManifestApplication_requiredForAllUsers
+ */
+ boolean isRequiredForAllUsers();
+
+ /**
+ * @see ApplicationInfo#requiresSmallestWidthDp
+ * @see R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
+ */
+ int getRequiresSmallestWidthDp();
+
+ /**
+ * SHA-512 hash of the only APK that can be used to update a system package.
+ * @see R.styleable#AndroidManifestRestrictUpdate
+ */
+ @Nullable
+ byte[] getRestrictUpdateHash();
+
+ /**
+ * The restricted account authenticator type that is used by this application
+ *
+ * @see PackageInfo#restrictedAccountType
+ * @see R.styleable#AndroidManifestApplication_restrictedAccountType
+ */
+ @Nullable
+ String getRestrictedAccountType();
+
+ /**
+ * @see ApplicationInfo#roundIconRes
+ * @see R.styleable#AndroidManifestApplication_roundIcon
+ */
+ int getRoundIconRes();
+
+ /**
+ * @see PackageInfo#sharedUserLabel
+ * @see R.styleable#AndroidManifest_sharedUserLabel
+ */
+ @Deprecated
+ int getSharedUserLabel();
+
+ /**
+ * The signature data of all APKs in this package, which must be exactly the same across the
+ * base and splits.
+ */
+ PackageParser.SigningDetails getSigningDetails();
+
+ /**
+ * @see ApplicationInfo#splitClassLoaderNames
+ * @see R.styleable#AndroidManifestApplication_classLoader
+ */
+ @Nullable
+ String[] getSplitClassLoaderNames();
+
+ /** @see R.styleable#AndroidManifestStaticLibrary_version */
+ long getStaticSharedLibVersion();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_largeScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_LARGE_SCREENS
+ */
+ boolean isSupportsLargeScreens();
+
+ /**
+ * If omitted from manifest, returns true.
+ * @see R.styleable#AndroidManifestSupportsScreens_normalScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_NORMAL_SCREENS
+ */
+ boolean isSupportsNormalScreens();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_smallScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_SMALL_SCREENS
+ */
+ boolean isSupportsSmallScreens();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD}.
+ * @see R.styleable#AndroidManifestSupportsScreens_xlargeScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_XLARGE_SCREENS
+ */
+ boolean isSupportsExtraLargeScreens();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING */
+ boolean isAllowNativeHeapPointerTagging();
+
+ boolean hasPreserveLegacyExternalStorage();
+
+ /**
+ * @see ApplicationInfo#targetSandboxVersion
+ * @see R.styleable#AndroidManifest_targetSandboxVersion
+ */
+ @Deprecated
+ int getTargetSandboxVersion();
+
+ /**
+ * @see ApplicationInfo#theme
+ * @see R.styleable#AndroidManifestApplication_theme
+ */
+ int getTheme();
+
+ /**
+ * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
+ * {@link PackageParser#TAG_KEY_SETS}.
+ * @see R.styleable#AndroidManifestUpgradeKeySet
+ */
+ @NonNull
+ Set<String> getUpgradeKeySets();
+
+ /**
+ * The install time abi override to choose 32bit abi's when multiple abi's
+ * are present. This is only meaningfull for multiarch applications.
+ * The use32bitAbi attribute is ignored if cpuAbiOverride is also set.
+ */
+ boolean isUse32BitAbi();
+
+ /** @see ApplicationInfo#volumeUuid */
+ @Nullable
+ String getVolumeUuid();
+
+ /** @see ApplicationInfo#zygotePreloadName */
+ @Nullable
+ String getZygotePreloadName();
+
+ /** Revision code of base APK */
+ int getBaseRevisionCode();
+
+ /** @see PackageInfo#versionName */
+ @Nullable
+ String getVersionName();
+
+ /** @see PackageInfo#versionCodeMajor */
+ @Nullable
+ int getVersionCode();
+
+ /** @see PackageInfo#versionCodeMajor */
+ @Nullable
+ int getVersionCodeMajor();
+
+ /**
+ * @see ApplicationInfo#compileSdkVersion
+ * @see R.styleable#AndroidManifest_compileSdkVersion
+ */
+ int getCompileSdkVersion();
+
+ /**
+ * @see ApplicationInfo#compileSdkVersionCodename
+ * @see R.styleable#AndroidManifest_compileSdkVersionCodename
+ */
+ @Nullable
+ String getCompileSdkVersionCodeName();
+
+ @Nullable
+ Set<String> getMimeGroups();
+
+ // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
+ ApplicationInfo toAppInfoWithoutState();
+}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
new file mode 100644
index 0000000..40754df
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -0,0 +1,2673 @@
+/*
+ * Copyright (C) 2020 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.parsing;
+
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
+import static android.os.Build.VERSION_CODES.DONUT;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
+import android.annotation.AnyRes;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleableRes;
+import android.app.ActivityThread;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.Signature;
+import android.content.pm.parsing.component.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedActivityUtils;
+import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedFeatureUtils;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedInstrumentationUtils;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedIntentInfoUtils;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedPermissionUtils;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProcessUtils;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedProviderUtils;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.pm.parsing.component.ParsedServiceUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
+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;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.ext.SdkExtensions;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.util.apk.ApkSignatureVerifier;
+
+import com.android.internal.R;
+import com.android.internal.os.ClassLoaderFactory;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+/**
+ * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it
+ * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate.
+ *
+ * @hide
+ */
+public class ParsingPackageUtils {
+
+ public static final String TAG = ParsingUtils.TAG;
+
+ private boolean mOnlyCoreApps;
+ private String[] mSeparateProcesses;
+ private DisplayMetrics mDisplayMetrics;
+ private Callback mCallback;
+
+ public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses,
+ DisplayMetrics displayMetrics, @NonNull Callback callback) {
+ mOnlyCoreApps = onlyCoreApps;
+ mSeparateProcesses = separateProcesses;
+ mDisplayMetrics = displayMetrics;
+ mCallback = callback;
+ }
+
+ /**
+ * Parse the package at the given location. Automatically detects if the
+ * package is a monolithic style (single APK file) or cluster style
+ * (directory of APKs).
+ * <p>
+ * This performs sanity checking on cluster style packages, such as
+ * requiring identical package name and version codes, a single base APK,
+ * and unique split names.
+ * <p>
+ * Note that this <em>does not</em> perform signature verification; that
+ * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+ *
+ * If {@code useCaches} is true, the package parser might return a cached
+ * result from a previous parse of the same {@code packageFile} with the same
+ * {@code flags}. Note that this method does not check whether {@code packageFile}
+ * has changed since the last parse, it's up to callers to do so.
+ *
+ * @see PackageParser#parsePackageLite(File, int)
+ */
+ public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
+ int flags)
+ throws PackageParserException {
+ if (packageFile.isDirectory()) {
+ return parseClusterPackage(input, packageFile, flags);
+ } else {
+ return parseMonolithicPackage(input, packageFile, flags);
+ }
+ }
+
+ /**
+ * Parse all APKs contained in the given directory, treating them as a
+ * single package. This also performs sanity checking, such as requiring
+ * identical package name and version codes, a single base APK, and unique
+ * split names.
+ * <p>
+ * Note that this <em>does not</em> perform signature verification; that
+ * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+ */
+ private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
+ int flags) throws PackageParserException {
+ final PackageParser.PackageLite lite = ApkLiteParseUtils.parseClusterPackageLite(packageDir,
+ 0);
+ if (mOnlyCoreApps && !lite.coreApp) {
+ return input.error("Not a coreApp: " + packageDir);
+ }
+
+ // Build the split dependency tree.
+ SparseArray<int[]> splitDependencies = null;
+ final SplitAssetLoader assetLoader;
+ if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+ try {
+ splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+ assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+ } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+ return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+ }
+ } else {
+ assetLoader = new DefaultSplitAssetLoader(lite, flags);
+ }
+
+ try {
+ final AssetManager assets = assetLoader.getBaseAssetManager();
+ final File baseApk = new File(lite.baseCodePath);
+ ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
+ lite.codePath, assets, flags);
+ // TODO(b/135203078): Pass original error up?
+ if (result.isError()) {
+ return input.error(INSTALL_PARSE_FAILED_NOT_APK,
+ "Failed to parse base APK: " + baseApk);
+ }
+
+ ParsingPackage pkg = result.getResult();
+ if (!ArrayUtils.isEmpty(lite.splitNames)) {
+ pkg.asSplit(
+ lite.splitNames,
+ lite.splitCodePaths,
+ lite.splitRevisionCodes,
+ splitDependencies
+ );
+ final int num = lite.splitNames.length;
+
+ for (int i = 0; i < num; i++) {
+ final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
+ parseSplitApk(input, pkg, i, splitAssets, flags);
+ }
+ }
+
+ pkg.setUse32BitAbi(lite.use32bitAbi);
+ return input.success(pkg);
+ } finally {
+ IoUtils.closeQuietly(assetLoader);
+ }
+ }
+
+ /**
+ * Parse the given APK file, treating it as as a single monolithic package.
+ * <p>
+ * Note that this <em>does not</em> perform signature verification; that
+ * must be done separately in {@link #collectCertificates(ParsingPackageRead, boolean)}.
+ */
+ private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
+ int flags) throws PackageParserException {
+ final PackageParser.PackageLite lite = ApkLiteParseUtils.parseMonolithicPackageLite(apkFile,
+ flags);
+ if (mOnlyCoreApps && !lite.coreApp) {
+ return input.error("Not a coreApp: " + apkFile);
+ }
+
+ final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
+ try {
+ ParseResult<ParsingPackage> result = parseBaseApk(input,
+ apkFile,
+ apkFile.getCanonicalPath(),
+ assetLoader.getBaseAssetManager(), flags);
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ return input.success(result.getResult()
+ .setUse32BitAbi(lite.use32bitAbi));
+ } catch (IOException e) {
+ return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to get path: " + apkFile, e);
+ } finally {
+ IoUtils.closeQuietly(assetLoader);
+ }
+ }
+
+ private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
+ String codePath, AssetManager assets, int flags) {
+ final String apkPath = apkFile.getAbsolutePath();
+
+ String volumeUuid = null;
+ if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
+ final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
+ volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
+ }
+
+ if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
+
+ try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
+ PackageParser.ANDROID_MANIFEST_FILENAME)) {
+ final Resources res = new Resources(assets, mDisplayMetrics, null);
+
+ ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
+ parser, flags);
+ if (result.isError()) {
+ return input.error(result.getErrorCode(),
+ apkPath + " (at " + parser.getPositionDescription() + "): "
+ + result.getErrorMessage());
+ }
+
+ ParsingPackage pkg = result.getResult();
+ ApkAssets apkAssets = assets.getApkAssets()[0];
+ if (apkAssets.definesOverlayable()) {
+ SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
+ int size = packageNames.size();
+ for (int index = 0; index < size; index++) {
+ String packageName = packageNames.get(index);
+ Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
+ if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
+ for (String overlayable : overlayableToActor.keySet()) {
+ pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
+ }
+ }
+ }
+ }
+
+ pkg.setVolumeUuid(volumeUuid)
+ .setSigningDetails(SigningDetails.UNKNOWN);
+
+ return input.success(pkg);
+ } catch (Exception e) {
+ return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to read manifest from " + apkPath, e);
+ }
+ }
+
+ private ParseResult<ParsingPackage> parseSplitApk(ParseInput input,
+ ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
+ final String apkPath = pkg.getSplitCodePaths()[splitIndex];
+
+ if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+
+ // This must always succeed, as the path has been added to the AssetManager before.
+ final int cookie = assets.findCookieForPath(apkPath);
+ if (cookie == 0) {
+ return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
+ try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
+ PackageParser.ANDROID_MANIFEST_FILENAME)) {
+ Resources res = new Resources(assets, mDisplayMetrics, null);
+ ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
+ parser, flags, splitIndex);
+ if (parseResult.isError()) {
+ return input.error(parseResult.getErrorCode(),
+ apkPath + " (at " + parser.getPositionDescription() + "): "
+ + parseResult.getErrorMessage());
+ }
+
+ return parseResult;
+ } catch (Exception e) {
+ return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+ "Failed to read manifest from " + apkPath, e);
+ }
+ }
+
+ /**
+ * Parse the manifest of a <em>base APK</em>. When adding new features you
+ * need to consider whether they should be supported by split APKs and child
+ * packages.
+ *
+ * @param apkPath The package apk file path
+ * @param res The resources from which to resolve values
+ * @param parser The manifest parser
+ * @param flags Flags how to parse
+ * @return Parsed package or null on error.
+ */
+ private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
+ String codePath, Resources res, XmlResourceParser parser, int flags)
+ throws XmlPullParserException, IOException {
+ final String splitName;
+ final String pkgName;
+
+ try {
+ Pair<String, String> packageSplit = PackageParser.parsePackageSplitNames(parser,
+ parser);
+ pkgName = packageSplit.first;
+ splitName = packageSplit.second;
+
+ if (!TextUtils.isEmpty(splitName)) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+ "Expected base APK, but found split " + splitName
+ );
+ }
+ } catch (PackageParserException e) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
+ }
+
+ TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
+ try {
+ boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
+
+ ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath,
+ manifestArray, isCoreApp);
+
+ ParseResult<ParsingPackage> result = parseBaseApkTags(input, pkg, manifestArray,
+ res, parser, flags);
+ if (result.isError()) {
+ return result;
+ }
+
+ return input.success(pkg);
+ } finally {
+ manifestArray.recycle();
+ }
+ }
+
+ /**
+ * Parse the manifest of a <em>split APK</em>.
+ * <p>
+ * Note that split APKs have many more restrictions on what they're capable
+ * of doing, so many valid features of a base APK have been carefully
+ * omitted here.
+ *
+ * @param pkg builder to fill
+ * @return false on failure
+ */
+ private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser, int flags, int splitIndex)
+ throws XmlPullParserException, IOException, PackageParserException {
+ AttributeSet attrs = parser;
+
+ // We parsed manifest tag earlier; just skip past it
+ PackageParser.parsePackageSplitNames(parser, attrs);
+
+ int type;
+
+ boolean foundApp = false;
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final ParseResult result;
+ String tagName = parser.getName();
+ if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+ if (foundApp) {
+ if (PackageParser.RIGID_PARSER) {
+ result = input.error("<manifest> has more than one <application>");
+ } else {
+ Slog.w(TAG, "<manifest> has more than one <application>");
+ result = input.success(null);
+ }
+ } else {
+ foundApp = true;
+ result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex);
+ }
+ } else {
+ result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ if (!foundApp) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY,
+ "<manifest> does not contain an <application>"
+ );
+ }
+
+ return input.success(pkg);
+ }
+
+ /**
+ * Parse the {@code application} XML tree at the current parse location in a
+ * <em>split APK</em> manifest.
+ * <p>
+ * Note that split APKs have many more restrictions on what they're capable
+ * of doing, so many valid features of a base APK have been carefully
+ * omitted here.
+ */
+ private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+ try {
+ pkg.setSplitHasCode(splitIndex, sa.getBoolean(
+ R.styleable.AndroidManifestApplication_hasCode, true));
+
+ final String classLoaderName = sa.getString(
+ R.styleable.AndroidManifestApplication_classLoader);
+ if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName(
+ classLoaderName)) {
+ pkg.setSplitClassLoaderName(splitIndex, classLoaderName);
+ } else {
+ return input.error("Invalid class loader name: " + classLoaderName);
+ }
+ } finally {
+ sa.recycle();
+ }
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ ParsedMainComponent mainComponent = null;
+
+ final ParseResult result;
+ String tagName = parser.getName();
+ boolean isActivity = false;
+ switch (tagName) {
+ case "activity":
+ isActivity = true;
+ // fall-through
+ case "receiver":
+ ParseResult<ParsedActivity> activityResult =
+ ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
+ res,
+ parser, flags, PackageParser.sUseRoundIcon, input);
+ if (activityResult.isSuccess()) {
+ ParsedActivity activity = activityResult.getResult();
+ if (isActivity) {
+ pkg.addActivity(activity);
+ } else {
+ pkg.addReceiver(activity);
+ }
+ mainComponent = activity;
+ }
+ result = activityResult;
+ break;
+ case "service":
+ ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
+ mSeparateProcesses, pkg, res, parser, flags,
+ PackageParser.sUseRoundIcon, input);
+ if (serviceResult.isSuccess()) {
+ ParsedService service = serviceResult.getResult();
+ pkg.addService(service);
+ mainComponent = service;
+ }
+ result = serviceResult;
+ break;
+ case "provider":
+ ParseResult<ParsedProvider> providerResult =
+ ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
+ flags, PackageParser.sUseRoundIcon, input);
+ if (providerResult.isSuccess()) {
+ ParsedProvider provider = providerResult.getResult();
+ pkg.addProvider(provider);
+ mainComponent = provider;
+ }
+ result = providerResult;
+ break;
+ case "activity-alias":
+ activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
+ PackageParser.sUseRoundIcon, input);
+ if (activityResult.isSuccess()) {
+ ParsedActivity activity = activityResult.getResult();
+ pkg.addActivity(activity);
+ mainComponent = activity;
+ }
+
+ result = activityResult;
+ break;
+ default:
+ result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser);
+ break;
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ if (mainComponent != null && mainComponent.getSplitName() == null) {
+ // If the loaded component did not specify a split, inherit the split name
+ // based on the split it is defined in.
+ // This is used to later load the correct split when starting this
+ // component.
+ mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]);
+ }
+ }
+
+ return input.success(pkg);
+ }
+
+ /**
+ * For parsing non-MainComponents. Main ones have an order and some special handling which is
+ * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources,
+ * XmlResourceParser, int, int)}.
+ */
+ private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+ switch (tag) {
+ case "meta-data":
+ // note: application meta-data is stored off to the side, so it can
+ // remain null in the primary copy (we like to avoid extra copies because
+ // it can be large)
+ ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
+ pkg.getMetaData(), input);
+ if (metaDataResult.isSuccess()) {
+ pkg.setMetaData(metaDataResult.getResult());
+ }
+ return metaDataResult;
+ case "uses-static-library":
+ return parseUsesStaticLibrary(input, pkg, res, parser);
+ case "uses-library":
+ return parseUsesLibrary(input, pkg, res, parser);
+ case "uses-package":
+ // Dependencies for app installers; we don't currently try to
+ // enforce this.
+ return input.success(null);
+ default:
+ return ParsingUtils.unknownTag("<application>", pkg, parser, input);
+ }
+ }
+
+ private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
+ TypedArray sa, Resources res, XmlResourceParser parser, int flags)
+ throws XmlPullParserException, IOException {
+ ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa);
+ if (sharedUserResult.isError()) {
+ return sharedUserResult;
+ }
+
+ pkg.setInstallLocation(anInt(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
+ R.styleable.AndroidManifest_installLocation, sa))
+ .setTargetSandboxVersion(anInt(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
+ R.styleable.AndroidManifest_targetSandboxVersion, sa))
+ /* Set the global "on SD card" flag */
+ .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0)
+ .setIsolatedSplitLoading(
+ bool(false, R.styleable.AndroidManifest_isolatedSplits, sa));
+
+ boolean foundApp = false;
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ final ParseResult result;
+
+ // TODO(b/135203078): Convert to instance methods to share variables
+ // <application> has special logic, so it's handled outside the general method
+ if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+ if (foundApp) {
+ if (PackageParser.RIGID_PARSER) {
+ result = input.error("<manifest> has more than one <application>");
+ } else {
+ Slog.w(TAG, "<manifest> has more than one <application>");
+ result = input.success(null);
+ }
+ } else {
+ foundApp = true;
+ result = parseBaseApplication(input, pkg, res, parser, flags);
+ }
+ } else {
+ result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY,
+ "<manifest> does not contain an <application> or <instrumentation>"
+ );
+ }
+
+ if (!ParsedFeature.isCombinationValid(pkg.getFeatures())) {
+ return input.error(
+ INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Combination <feature> tags are not valid"
+ );
+ }
+
+ convertNewPermissions(pkg);
+
+ convertSplitPermissions(pkg);
+
+ // At this point we can check if an application is not supporting densities and hence
+ // cannot be windowed / resized. Note that an SDK version of 0 is common for
+ // pre-Doughnut applications.
+ if (pkg.getTargetSdkVersion() < DONUT
+ || (!pkg.isSupportsSmallScreens()
+ && !pkg.isSupportsNormalScreens()
+ && !pkg.isSupportsLargeScreens()
+ && !pkg.isSupportsExtraLargeScreens()
+ && !pkg.isResizeable()
+ && !pkg.isAnyDensity())) {
+ adjustPackageToBeUnresizeableAndUnpipable(pkg);
+ }
+
+ return input.success(pkg);
+ }
+
+ private ParseResult parseBaseApkTag(String tag, ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
+ throws IOException, XmlPullParserException {
+ switch (tag) {
+ case PackageParser.TAG_OVERLAY:
+ return parseOverlay(input, pkg, res, parser);
+ case PackageParser.TAG_KEY_SETS:
+ return parseKeySets(input, pkg, res, parser);
+ case PackageParser.TAG_FEATURE:
+ return parseFeature(input, pkg, res, parser);
+ case PackageParser.TAG_PERMISSION_GROUP:
+ return parsePermissionGroup(input, pkg, res, parser);
+ case PackageParser.TAG_PERMISSION:
+ return parsePermission(input, pkg, res, parser);
+ case PackageParser.TAG_PERMISSION_TREE:
+ return parsePermissionTree(input, pkg, res, parser);
+ case PackageParser.TAG_USES_PERMISSION:
+ case PackageParser.TAG_USES_PERMISSION_SDK_M:
+ case PackageParser.TAG_USES_PERMISSION_SDK_23:
+ return parseUsesPermission(input, pkg, res, parser);
+ case PackageParser.TAG_USES_CONFIGURATION:
+ return parseUsesConfiguration(input, pkg, res, parser);
+ case PackageParser.TAG_USES_FEATURE:
+ return parseUsesFeature(input, pkg, res, parser);
+ case PackageParser.TAG_FEATURE_GROUP:
+ return parseFeatureGroup(input, pkg, res, parser);
+ case PackageParser.TAG_USES_SDK:
+ return parseUsesSdk(input, pkg, res, parser);
+ case PackageParser.TAG_SUPPORT_SCREENS:
+ return parseSupportScreens(input, pkg, res, parser);
+ case PackageParser.TAG_PROTECTED_BROADCAST:
+ return parseProtectedBroadcast(input, pkg, res, parser);
+ case PackageParser.TAG_INSTRUMENTATION:
+ return parseInstrumentation(input, pkg, res, parser);
+ case PackageParser.TAG_ORIGINAL_PACKAGE:
+ return parseOriginalPackage(input, pkg, res, parser);
+ case PackageParser.TAG_ADOPT_PERMISSIONS:
+ return parseAdoptPermissions(input, pkg, res, parser);
+ case PackageParser.TAG_USES_GL_TEXTURE:
+ case PackageParser.TAG_COMPATIBLE_SCREENS:
+ case PackageParser.TAG_SUPPORTS_INPUT:
+ case PackageParser.TAG_EAT_COMMENT:
+ // Just skip this tag
+ XmlUtils.skipCurrentTag(parser);
+ return input.success(pkg);
+ case PackageParser.TAG_RESTRICT_UPDATE:
+ return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+ case PackageParser.TAG_QUERIES:
+ return parseQueries(input, pkg, res, parser);
+ default:
+ return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input,
+ ParsingPackage pkg, TypedArray sa) {
+ String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa);
+ if (TextUtils.isEmpty(str)) {
+ return input.success(pkg);
+ }
+
+ ParseResult nameResult = validateName(input, str, true, true);
+ if (nameResult.isError()) {
+ if ("android".equals(pkg.getPackageName())) {
+ nameResult.ignoreError();
+ } else {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+ "<manifest> specifies bad sharedUserId name \"" + str + "\": "
+ + nameResult.getErrorMessage());
+ }
+ }
+
+ return input.success(pkg
+ .setSharedUserId(str.intern())
+ .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa)));
+ }
+
+ private static ParseResult<ParsingPackage> parseKeySets(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ // we've encountered the 'key-sets' tag
+ // all the keys and keysets that we want must be defined here
+ // so we're going to iterate over the parser and pull out the things we want
+ int outerDepth = parser.getDepth();
+ int currentKeySetDepth = -1;
+ int type;
+ String currentKeySet = null;
+ ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>();
+ ArraySet<String> upgradeKeySets = new ArraySet<>();
+ ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>();
+ ArraySet<String> improperKeySets = new ArraySet<>();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG) {
+ if (parser.getDepth() == currentKeySetDepth) {
+ currentKeySet = null;
+ currentKeySetDepth = -1;
+ }
+ continue;
+ }
+ String tagName = parser.getName();
+ switch (tagName) {
+ case "key-set": {
+ if (currentKeySet != null) {
+ return input.error("Improperly nested 'key-set' tag at "
+ + parser.getPositionDescription());
+ }
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet);
+ try {
+ final String keysetName = sa.getNonResourceString(
+ R.styleable.AndroidManifestKeySet_name);
+ definedKeySets.put(keysetName, new ArraySet<>());
+ currentKeySet = keysetName;
+ currentKeySetDepth = parser.getDepth();
+ } finally {
+ sa.recycle();
+ }
+ } break;
+ case "public-key": {
+ if (currentKeySet == null) {
+ return input.error("Improperly nested 'key-set' tag at "
+ + parser.getPositionDescription());
+ }
+ TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestPublicKey);
+ try {
+ final String publicKeyName = nonResString(
+ R.styleable.AndroidManifestPublicKey_name, sa);
+ final String encodedKey = nonResString(
+ R.styleable.AndroidManifestPublicKey_value, sa);
+ if (encodedKey == null && publicKeys.get(publicKeyName) == null) {
+ return input.error("'public-key' " + publicKeyName
+ + " must define a public-key value on first use at "
+ + parser.getPositionDescription());
+ } else if (encodedKey != null) {
+ PublicKey currentKey = PackageParser.parsePublicKey(encodedKey);
+ if (currentKey == null) {
+ Slog.w(TAG, "No recognized valid key in 'public-key' tag at "
+ + parser.getPositionDescription() + " key-set "
+ + currentKeySet
+ + " will not be added to the package's defined key-sets.");
+ improperKeySets.add(currentKeySet);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ if (publicKeys.get(publicKeyName) == null
+ || publicKeys.get(publicKeyName).equals(currentKey)) {
+
+ /* public-key first definition, or matches old definition */
+ publicKeys.put(publicKeyName, currentKey);
+ } else {
+ return input.error("Value of 'public-key' " + publicKeyName
+ + " conflicts with previously defined value at "
+ + parser.getPositionDescription());
+ }
+ }
+ definedKeySets.get(currentKeySet).add(publicKeyName);
+ XmlUtils.skipCurrentTag(parser);
+ } finally {
+ sa.recycle();
+ }
+ } break;
+ case "upgrade-key-set": {
+ TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestUpgradeKeySet);
+ try {
+ String name = sa.getNonResourceString(
+ R.styleable.AndroidManifestUpgradeKeySet_name);
+ upgradeKeySets.add(name);
+ XmlUtils.skipCurrentTag(parser);
+ } finally {
+ sa.recycle();
+ }
+ } break;
+ default:
+ ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser,
+ input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ break;
+ }
+ }
+ String packageName = pkg.getPackageName();
+ Set<String> publicKeyNames = publicKeys.keySet();
+ if (publicKeyNames.removeAll(definedKeySets.keySet())) {
+ return input.error("Package" + packageName
+ + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct.");
+ }
+
+ for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) {
+ final String keySetName = e.getKey();
+ if (e.getValue().size() == 0) {
+ Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+ + "'key-set' " + keySetName + " has no valid associated 'public-key'."
+ + " Not including in package's defined key-sets.");
+ continue;
+ } else if (improperKeySets.contains(keySetName)) {
+ Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml "
+ + "'key-set' " + keySetName + " contained improper 'public-key'"
+ + " tags. Not including in package's defined key-sets.");
+ continue;
+ }
+
+ for (String s : e.getValue()) {
+ pkg.addKeySet(keySetName, publicKeys.get(s));
+ }
+ }
+ if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) {
+ pkg.setUpgradeKeySets(upgradeKeySets);
+ } else {
+ return input.error("Package" + packageName
+ + " AndroidManifest.xml does not define all 'upgrade-key-set's .");
+ }
+
+ return input.success(pkg);
+ }
+
+ private static ParseResult<ParsingPackage> parseFeature(ParseInput input, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+ ParseResult<ParsedFeature> result = ParsedFeatureUtils.parseFeature(res, parser, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ return input.success(pkg.addFeature(result.getResult()));
+ }
+
+ private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
+ pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ return input.success(pkg.addPermissionGroup(result.getResult()));
+ }
+
+ private static ParseResult<ParsingPackage> parsePermission(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
+ pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ return input.success(pkg.addPermission(result.getResult()));
+ }
+
+ private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
+ pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ return input.success(pkg.addPermission(result.getResult()));
+ }
+
+ private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String name = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesPermission_name);
+
+ int maxSdkVersion = 0;
+ TypedValue val = sa.peekValue(
+ R.styleable.AndroidManifestUsesPermission_maxSdkVersion);
+ if (val != null) {
+ if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) {
+ maxSdkVersion = val.data;
+ }
+ }
+
+ final String requiredFeature = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
+
+ final String requiredNotfeature = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
+ 0);
+
+ XmlUtils.skipCurrentTag(parser);
+
+ // Can only succeed from here on out
+ ParseResult<ParsingPackage> success = input.success(pkg);
+
+ if (name == null) {
+ return success;
+ }
+
+ if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) {
+ return success;
+ }
+
+ // Only allow requesting this permission if the platform supports the given feature.
+ if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(
+ requiredFeature)) {
+ return success;
+ }
+
+ // Only allow requesting this permission if the platform doesn't support the given
+ // feature.
+ if (requiredNotfeature != null && mCallback != null
+ && mCallback.hasFeature(requiredNotfeature)) {
+ return success;
+ }
+
+ if (!pkg.getRequestedPermissions().contains(name)) {
+ pkg.addRequestedPermission(name.intern());
+ } else {
+ Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: "
+ + name + " in package: " + pkg.getPackageName() + " at: "
+ + parser.getPositionDescription());
+ }
+
+ return success;
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ ConfigurationInfo cPref = new ConfigurationInfo();
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration);
+ try {
+ cPref.reqTouchScreen = sa.getInt(
+ R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
+ Configuration.TOUCHSCREEN_UNDEFINED);
+ cPref.reqKeyboardType = sa.getInt(
+ R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
+ Configuration.KEYBOARD_UNDEFINED);
+ if (sa.getBoolean(
+ R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
+ false)) {
+ cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
+ }
+ cPref.reqNavigation = sa.getInt(
+ R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
+ Configuration.NAVIGATION_UNDEFINED);
+ if (sa.getBoolean(
+ R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
+ false)) {
+ cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
+ }
+ pkg.addConfigPreference(cPref);
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ FeatureInfo fi = parseFeatureInfo(res, parser);
+ pkg.addReqFeature(fi);
+
+ if (fi.name == null) {
+ ConfigurationInfo cPref = new ConfigurationInfo();
+ cPref.reqGlEsVersion = fi.reqGlEsVersion;
+ pkg.addConfigPreference(cPref);
+ }
+
+ return input.success(pkg);
+ }
+
+ private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) {
+ FeatureInfo fi = new FeatureInfo();
+ TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name);
+ fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0);
+ if (fi.name == null) {
+ fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion,
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ }
+ if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) {
+ fi.flags |= FeatureInfo.FLAG_REQUIRED;
+ }
+ return fi;
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ FeatureGroupInfo group = new FeatureGroupInfo();
+ ArrayList<FeatureInfo> features = null;
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String innerTagName = parser.getName();
+ if (innerTagName.equals("uses-feature")) {
+ FeatureInfo featureInfo = parseFeatureInfo(res, parser);
+ // FeatureGroups are stricter and mandate that
+ // any <uses-feature> declared are mandatory.
+ featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+ features = ArrayUtils.add(features, featureInfo);
+ } else {
+ Slog.w(TAG,
+ "Unknown element under <feature-group>: " + innerTagName +
+ " at " + pkg.getBaseCodePath() + " " +
+ parser.getPositionDescription());
+ }
+ }
+
+ if (features != null) {
+ group.features = new FeatureInfo[features.size()];
+ group.features = features.toArray(group.features);
+ }
+
+ pkg.addFeatureGroup(group);
+ return input.success(pkg);
+ }
+
+ private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ if (PackageParser.SDK_VERSION > 0) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
+ try {
+ int minVers = 1;
+ String minCode = null;
+ int targetVers = 0;
+ String targetCode = null;
+
+ TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+ if (val != null) {
+ if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+ minCode = val.string.toString();
+ } else {
+ // If it's not a string, it's an integer.
+ minVers = val.data;
+ }
+ }
+
+ val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+ if (val != null) {
+ if (val.type == TypedValue.TYPE_STRING && val.string != null) {
+ targetCode = val.string.toString();
+ if (minCode == null) {
+ minCode = targetCode;
+ }
+ } else {
+ // If it's not a string, it's an integer.
+ targetVers = val.data;
+ }
+ } else {
+ targetVers = minVers;
+ targetCode = minCode;
+ }
+
+ ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
+ PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input);
+ if (minSdkVersionResult.isError()) {
+ return input.error(minSdkVersionResult);
+ }
+
+ int minSdkVersion = minSdkVersionResult.getResult();
+
+ ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
+ targetVers, targetCode, PackageParser.SDK_CODENAMES, input);
+ if (targetSdkVersionResult.isError()) {
+ return input.error(targetSdkVersionResult);
+ }
+
+ int targetSdkVersion = minSdkVersionResult.getResult();
+
+ pkg.setMinSdkVersion(minSdkVersion)
+ .setTargetSdkVersion(targetSdkVersion);
+
+ int type;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ final ParseResult result;
+ if (parser.getName().equals("extension-sdk")) {
+ result = parseExtensionSdk(input, pkg, res, parser);
+ XmlUtils.skipCurrentTag(parser);
+ } else {
+ result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input);
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+ } finally {
+ sa.recycle();
+ }
+ }
+ return input.success(pkg);
+ }
+
+ private static ParseResult parseExtensionSdk(ParseInput input, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser) {
+ int sdkVersion;
+ int minVersion;
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk);
+ try {
+ sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1);
+ minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1);
+ } finally {
+ sa.recycle();
+ }
+
+ if (sdkVersion < 0) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "<extension-sdk> must specify an sdkVersion >= 0");
+ }
+ if (minVersion < 0) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "<extension-sdk> must specify minExtensionVersion >= 0");
+ }
+
+ try {
+ int version = SdkExtensions.getExtensionVersion(sdkVersion);
+ if (version < minVersion) {
+ return input.error(
+ PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Package requires " + sdkVersion + " extension version " + minVersion
+ + " which exceeds device version " + version);
+ }
+ } catch (RuntimeException e) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Specified sdkVersion " + sdkVersion + " is not valid");
+ }
+ return input.success(pkg);
+ }
+
+ /**
+ * {@link ParseResult} version of
+ * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])}
+ */
+ public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers,
+ @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion,
+ @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) {
+ // If it's a release SDK, make sure we meet the minimum SDK requirement.
+ if (minCode == null) {
+ if (minVers <= platformSdkVersion) {
+ return input.success(minVers);
+ }
+
+ // We don't meet the minimum SDK requirement.
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Requires newer sdk version #" + minVers
+ + " (current version is #" + platformSdkVersion + ")");
+ }
+
+ // If it's a pre-release SDK and the codename matches this platform, we
+ // definitely meet the minimum SDK requirement.
+ if (matchTargetCode(platformSdkCodenames, minCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ // Otherwise, we're looking at an incompatible pre-release SDK.
+ if (platformSdkCodenames.length > 0) {
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Requires development platform " + minCode
+ + " (current platform is any of "
+ + Arrays.toString(platformSdkCodenames) + ")");
+ } else {
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Requires development platform " + minCode
+ + " but this is a release platform.");
+ }
+ }
+
+ /**
+ * {@link ParseResult} version of
+ * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])}
+ */
+ public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers,
+ @Nullable String targetCode, @NonNull String[] platformSdkCodenames,
+ @NonNull ParseInput input) {
+ // If it's a release SDK, return the version number unmodified.
+ if (targetCode == null) {
+ return input.success(targetVers);
+ }
+
+ // If it's a pre-release SDK and the codename matches this platform, it
+ // definitely targets this SDK.
+ if (matchTargetCode(platformSdkCodenames, targetCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ // Otherwise, we're looking at an incompatible pre-release SDK.
+ if (platformSdkCodenames.length > 0) {
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Requires development platform " + targetCode
+ + " (current platform is any of "
+ + Arrays.toString(platformSdkCodenames) + ")");
+ } else {
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
+ "Requires development platform " + targetCode
+ + " but this is a release platform.");
+ }
+ }
+
+ /**
+ * Matches a given {@code targetCode} against a set of release codeNames. Target codes can
+ * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form
+ * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}).
+ */
+ private static boolean matchTargetCode(@NonNull String[] codeNames,
+ @NonNull String targetCode) {
+ final String targetCodeName;
+ final int targetCodeIdx = targetCode.indexOf('.');
+ if (targetCodeIdx == -1) {
+ targetCodeName = targetCode;
+ } else {
+ targetCodeName = targetCode.substring(0, targetCodeIdx);
+ }
+ return ArrayUtils.contains(codeNames, targetCodeName);
+ }
+
+ private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
+ try {
+ final String hash = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestRestrictUpdate_hash,
+ 0);
+
+ if (hash != null) {
+ final int hashLength = hash.length();
+ final byte[] hashBytes = new byte[hashLength / 2];
+ for (int i = 0; i < hashLength; i += 2) {
+ hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16)
+ << 4)
+ + Character.digit(hash.charAt(i + 1), 16));
+ }
+ pkg.setRestrictUpdateHash(hashBytes);
+ } else {
+ pkg.setRestrictUpdateHash(null);
+ }
+ } finally {
+ sa.recycle();
+ }
+ }
+ return input.success(pkg);
+ }
+
+ private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ if (parser.getName().equals("intent")) {
+ ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null,
+ pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ ParsedIntentInfo intentInfo = result.getResult();
+
+ Uri data = null;
+ String dataType = null;
+ String host = "";
+ final int numActions = intentInfo.countActions();
+ final int numSchemes = intentInfo.countDataSchemes();
+ final int numTypes = intentInfo.countDataTypes();
+ final int numHosts = intentInfo.getHosts().length;
+ if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) {
+ return input.error("intent tags must contain either an action or data.");
+ }
+ if (numActions > 1) {
+ return input.error("intent tag may have at most one action.");
+ }
+ if (numTypes > 1) {
+ return input.error("intent tag may have at most one data type.");
+ }
+ if (numSchemes > 1) {
+ return input.error("intent tag may have at most one data scheme.");
+ }
+ if (numHosts > 1) {
+ return input.error("intent tag may have at most one data host.");
+ }
+ Intent intent = new Intent();
+ for (int i = 0, max = intentInfo.countCategories(); i < max; i++) {
+ intent.addCategory(intentInfo.getCategory(i));
+ }
+ if (numHosts == 1) {
+ host = intentInfo.getHosts()[0];
+ }
+ if (numSchemes == 1) {
+ data = new Uri.Builder()
+ .scheme(intentInfo.getDataScheme(0))
+ .authority(host)
+ .build();
+ }
+ if (numTypes == 1) {
+ dataType = intentInfo.getDataType(0);
+ }
+ intent.setDataAndType(data, dataType);
+ if (numActions == 1) {
+ intent.setAction(intentInfo.getAction(0));
+ }
+ pkg.addQueriesIntent(intent);
+ } else if (parser.getName().equals("package")) {
+ final TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestQueriesPackage);
+ final String packageName = sa.getString(
+ R.styleable.AndroidManifestQueriesPackage_name);
+ if (TextUtils.isEmpty(packageName)) {
+ return input.error("Package name is missing from package tag.");
+ }
+ pkg.addQueriesPackage(packageName.intern());
+ } else if (parser.getName().equals("provider")) {
+ final TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestQueriesProvider);
+ try {
+ final String authorities =
+ sa.getString(R.styleable.AndroidManifestQueriesProvider_authorities);
+ if (TextUtils.isEmpty(authorities)) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Authority missing from provider tag."
+ );
+ }
+ StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
+ while (authoritiesTokenizer.hasMoreElements()) {
+ pkg.addQueriesProvider(authoritiesTokenizer.nextToken());
+ }
+ } finally {
+ sa.recycle();
+ }
+ }
+ }
+ return input.success(pkg);
+ }
+
+ /**
+ * Parse the {@code application} XML tree at the current parse location in a
+ * <em>base APK</em> manifest.
+ * <p>
+ * When adding new features, carefully consider if they should also be
+ * supported by split APKs.
+ *
+ * This method should avoid using a getter for fields set by this method. Prefer assigning
+ * a local variable and using it. Otherwise there's an ordering problem which can be broken
+ * if any code moves around.
+ */
+ private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
+ throws XmlPullParserException, IOException {
+ final String pkgName = pkg.getPackageName();
+ int targetSdk = pkg.getTargetSdkVersion();
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
+ try {
+ // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
+ // This case can only happen in unit tests where we sometimes need to create fakes
+ // of various package parser data structures.
+ if (sa == null) {
+ return input.error("<application> does not contain any attributes");
+ }
+
+ String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
+ 0);
+ if (name != null) {
+ String packageName = pkg.getPackageName();
+ String outInfoName = ParsingUtils.buildClassName(packageName, name);
+ if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
+ return input.error("<application> invalid android:name");
+ } else if (outInfoName == null) {
+ return input.error("Empty class name in package " + packageName);
+ }
+
+ pkg.setClassName(outInfoName);
+ }
+
+ TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
+ if (labelValue != null) {
+ pkg.setLabelRes(labelValue.resourceId);
+ if (labelValue.resourceId == 0) {
+ pkg.setNonLocalizedLabel(labelValue.coerceToString());
+ }
+ }
+
+ parseBaseAppBasicFlags(pkg, sa);
+
+ String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
+ R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
+ if (manageSpaceActivity != null) {
+ String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
+ manageSpaceActivity);
+
+ if (manageSpaceActivityName == null) {
+ return input.error("Empty class name in package " + pkgName);
+ }
+
+ pkg.setManageSpaceActivityName(manageSpaceActivityName);
+ }
+
+ if (pkg.isAllowBackup()) {
+ // backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
+ // and restoreAnyVersion are only relevant if backup is possible for the
+ // given application.
+ String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
+ R.styleable.AndroidManifestApplication_backupAgent, sa);
+ if (backupAgent != null) {
+ String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
+ if (backupAgentName == null) {
+ return input.error("Empty class name in package " + pkgName);
+ }
+
+ if (PackageParser.DEBUG_BACKUP) {
+ Slog.v(TAG, "android:backupAgent = " + backupAgentName
+ + " from " + pkgName + "+" + backupAgent);
+ }
+
+ pkg.setBackupAgentName(backupAgentName)
+ .setKillAfterRestore(bool(true,
+ R.styleable.AndroidManifestApplication_killAfterRestore, sa))
+ .setRestoreAnyVersion(bool(false,
+ R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
+ .setFullBackupOnly(bool(false,
+ R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
+ .setBackupInForeground(bool(false,
+ R.styleable.AndroidManifestApplication_backupInForeground, sa));
+ }
+
+ TypedValue v = sa.peekValue(
+ R.styleable.AndroidManifestApplication_fullBackupContent);
+ int fullBackupContent = 0;
+
+ if (v != null) {
+ fullBackupContent = v.resourceId;
+
+ if (v.resourceId == 0) {
+ if (PackageParser.DEBUG_BACKUP) {
+ Slog.v(TAG, "fullBackupContent specified as boolean=" +
+ (v.data == 0 ? "false" : "true"));
+ }
+ // "false" => -1, "true" => 0
+ fullBackupContent = v.data == 0 ? -1 : 0;
+ }
+
+ pkg.setFullBackupContent(fullBackupContent);
+ }
+ if (PackageParser.DEBUG_BACKUP) {
+ Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
+ }
+ }
+
+ if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
+ // Check if persistence is based on a feature being present
+ final String requiredFeature = sa.getNonResourceString(R.styleable
+ .AndroidManifestApplication_persistentWhenFeatureAvailable);
+ pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
+ }
+
+ // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a
+ // util or just have PackageImpl return true if either flag is set
+ // Debuggable implies profileable
+ pkg.setProfileableByShell(pkg.isProfileableByShell() || pkg.isDebuggable());
+
+ if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
+ pkg.setResizeableActivity(sa.getBoolean(
+ R.styleable.AndroidManifestApplication_resizeableActivity, true));
+ } else {
+ pkg.setResizeableActivityViaSdkVersion(
+ targetSdk >= Build.VERSION_CODES.N);
+ }
+
+ String taskAffinity;
+ if (targetSdk >= Build.VERSION_CODES.FROYO) {
+ taskAffinity = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestApplication_taskAffinity,
+ Configuration.NATIVE_CONFIG_VERSION);
+ } else {
+ // Some older apps have been seen to use a resource reference
+ // here that on older builds was ignored (with a warning). We
+ // need to continue to do this for them so they don't break.
+ taskAffinity = sa.getNonResourceString(
+ R.styleable.AndroidManifestApplication_taskAffinity);
+ }
+
+ ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
+ pkgName, pkgName, taskAffinity, input);
+ if (taskAffinityResult.isError()) {
+ return input.error(taskAffinityResult);
+ }
+
+ pkg.setTaskAffinity(taskAffinityResult.getResult());
+ String factory = sa.getNonResourceString(
+ R.styleable.AndroidManifestApplication_appComponentFactory);
+ if (factory != null) {
+ String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
+ if (appComponentFactory == null) {
+ return input.error("Empty class name in package " + pkgName);
+ }
+
+ pkg.setAppComponentFactory(appComponentFactory);
+ }
+
+ CharSequence pname;
+ if (targetSdk >= Build.VERSION_CODES.FROYO) {
+ pname = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestApplication_process,
+ Configuration.NATIVE_CONFIG_VERSION);
+ } else {
+ // Some older apps have been seen to use a resource reference
+ // here that on older builds was ignored (with a warning). We
+ // need to continue to do this for them so they don't break.
+ pname = sa.getNonResourceString(
+ R.styleable.AndroidManifestApplication_process);
+ }
+ ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+ pkgName, null, pname, flags, mSeparateProcesses, input);
+ if (processNameResult.isError()) {
+ return input.error(processNameResult);
+ }
+
+ String processName = processNameResult.getResult();
+ pkg.setProcessName(processName);
+
+ if (pkg.isCantSaveState()) {
+ // A heavy-weight application can not be in a custom process.
+ // We can do direct compare because we intern all strings.
+ if (processName != null && !processName.equals(pkgName)) {
+ return input.error(
+ "cantSaveState applications can not use custom processes");
+ }
+ }
+
+ String classLoaderName = pkg.getClassLoaderName();
+ if (classLoaderName != null
+ && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
+ return input.error("Invalid class loader name: " + classLoaderName);
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ boolean hasActivityOrder = false;
+ boolean hasReceiverOrder = false;
+ boolean hasServiceOrder = false;
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final ParseResult result;
+ String tagName = parser.getName();
+ boolean isActivity = false;
+ switch (tagName) {
+ case "activity":
+ isActivity = true;
+ // fall-through
+ case "receiver":
+ ParseResult<ParsedActivity> activityResult =
+ ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
+ res, parser, flags, PackageParser.sUseRoundIcon, input);
+
+ if (activityResult.isSuccess()) {
+ ParsedActivity activity = activityResult.getResult();
+ if (isActivity) {
+ hasActivityOrder |= (activity.getOrder() != 0);
+ pkg.addActivity(activity);
+ } else {
+ hasReceiverOrder |= (activity.getOrder() != 0);
+ pkg.addReceiver(activity);
+ }
+ }
+
+ result = activityResult;
+ break;
+ case "service":
+ ParseResult<ParsedService> serviceResult =
+ ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
+ flags, PackageParser.sUseRoundIcon, input);
+ if (serviceResult.isSuccess()) {
+ ParsedService service = serviceResult.getResult();
+ hasServiceOrder |= (service.getOrder() != 0);
+ pkg.addService(service);
+ }
+
+ result = serviceResult;
+ break;
+ case "provider":
+ ParseResult<ParsedProvider> providerResult =
+ ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
+ flags, PackageParser.sUseRoundIcon, input);
+ if (providerResult.isSuccess()) {
+ pkg.addProvider(providerResult.getResult());
+ }
+
+ result = providerResult;
+ break;
+ case "activity-alias":
+ activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
+ parser, PackageParser.sUseRoundIcon, input);
+ if (activityResult.isSuccess()) {
+ ParsedActivity activity = activityResult.getResult();
+ hasActivityOrder |= (activity.getOrder() != 0);
+ pkg.addActivity(activity);
+ }
+
+ result = activityResult;
+ break;
+ default:
+ result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
+ break;
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
+ // Add a hidden app detail activity to normal apps which forwards user to App Details
+ // page.
+ ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
+ // Backwards-compat, assume success
+ pkg.addActivity(a.getResult());
+ a.ignoreError();
+ }
+
+ if (hasActivityOrder) {
+ pkg.sortActivities();
+ }
+ if (hasReceiverOrder) {
+ pkg.sortReceivers();
+ }
+ if (hasServiceOrder) {
+ pkg.sortServices();
+ }
+
+ // Must be run 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(pkg);
+ setMinAspectRatio(pkg);
+
+ pkg.setHasDomainUrls(hasDomainURLs(pkg));
+
+ return input.success(pkg);
+ }
+
+ /**
+ * Collection of single-line, no (or little) logic assignments. Separated for readability.
+ *
+ * Flags are separated by type and by default value. They are sorted alphabetically within each
+ * section.
+ */
+ private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
+ int targetSdk = pkg.getTargetSdkVersion();
+ //@formatter:off
+ // CHECKSTYLE:off
+ pkg
+ // Default true
+ .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
+ .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
+ .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
+ .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
+ .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
+ .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
+ .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
+ // Default false
+ .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
+ .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
+ .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa))
+ .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa))
+ .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa))
+ .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa))
+ .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa))
+ .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa))
+ .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa))
+ .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa))
+ .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa))
+ .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa))
+ .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa))
+ .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa))
+ .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
+ .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
+ .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
+ // targetSdkVersion gated
+ .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
+ .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
+ .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa))
+ .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa))
+ // Ints Default 0
+ .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa))
+ // Ints
+ .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa))
+ // Floats Default 0f
+ .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa))
+ .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa))
+ // Resource ID
+ .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa))
+ .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
+ .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))
+ .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
+ .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
+ .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
+ .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
+ // Strings
+ .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
+ .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
+ .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
+ .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
+ // Non-Config String
+ .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
+ // CHECKSTYLE:on
+ //@formatter:on
+ }
+
+ /**
+ * For parsing non-MainComponents. Main ones have an order and some special handling which is
+ * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources,
+ * XmlResourceParser, int)}.
+ */
+ private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser, int flags)
+ throws IOException, XmlPullParserException {
+ switch (tag) {
+ case "meta-data":
+ // TODO(b/135203078): I have no idea what this comment means
+ // note: application meta-data is stored off to the side, so it can
+ // remain null in the primary copy (we like to avoid extra copies because
+ // it can be large)
+ ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
+ pkg.getMetaData(), input);
+ if (metaDataResult.isSuccess()) {
+ pkg.setMetaData(metaDataResult.getResult());
+ }
+
+ return metaDataResult;
+ case "static-library":
+ return parseStaticLibrary(pkg, res, parser, input);
+ case "library":
+ return parseLibrary(pkg, res, parser, input);
+ case "uses-static-library":
+ return parseUsesStaticLibrary(input, pkg, res, parser);
+ case "uses-library":
+ return parseUsesLibrary(input, pkg, res, parser);
+ case "processes":
+ return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags);
+ case "uses-package":
+ // Dependencies for app installers; we don't currently try to
+ // enforce this.
+ return input.success(null);
+ case "profileable":
+ return parseProfileable(input, pkg, res, parser);
+ default:
+ return ParsingUtils.unknownTag("<application>", pkg, parser, input);
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ParsingPackage> parseStaticLibrary(
+ ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, ParseInput input) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestStaticLibrary_name);
+ final int version = sa.getInt(
+ R.styleable.AndroidManifestStaticLibrary_version, -1);
+ final int versionMajor = sa.getInt(
+ R.styleable.AndroidManifestStaticLibrary_versionMajor,
+ 0);
+
+ // Since the app canot run without a static lib - fail if malformed
+ if (lname == null || version < 0) {
+ return input.error("Bad static-library declaration name: " + lname
+ + " version: " + version);
+ } else if (pkg.getSharedUserId() != null) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+ "sharedUserId not allowed in static shared library"
+ );
+ } else if (pkg.getStaticSharedLibName() != null) {
+ return input.error("Multiple static-shared libs for package "
+ + pkg.getPackageName());
+ }
+
+ return input.success(pkg.setStaticSharedLibName(lname.intern())
+ .setStaticSharedLibVersion(
+ PackageInfo.composeLongVersionCode(versionMajor, version))
+ .setStaticSharedLibrary(true));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ParsingPackage> parseLibrary(
+ ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, ParseInput input) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name);
+
+ if (lname != null) {
+ lname = lname.intern();
+ if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) {
+ pkg.addLibraryName(lname);
+ }
+ }
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesLibrary_name);
+ final int version = sa.getInt(
+ R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
+ String certSha256Digest = sa.getNonResourceString(R.styleable
+ .AndroidManifestUsesStaticLibrary_certDigest);
+
+ // Since an APK providing a static shared lib can only provide the lib - fail if
+ // malformed
+ if (lname == null || version < 0 || certSha256Digest == null) {
+ return input.error("Bad uses-static-library declaration name: " + lname
+ + " version: " + version + " certDigest" + certSha256Digest);
+ }
+
+ // Can depend only on one version of the same library
+ List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+ if (usesStaticLibraries.contains(lname)) {
+ return input.error(
+ "Depending on multiple versions of static library " + lname);
+ }
+
+ lname = lname.intern();
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+ // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
+ String[] additionalCertSha256Digests = EmptyArray.STRING;
+ if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
+ ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+ if (certResult.isError()) {
+ return input.error(certResult);
+ }
+ additionalCertSha256Digests = certResult.getResult();
+ }
+
+ final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+ certSha256Digests[0] = certSha256Digest;
+ System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+ 1, additionalCertSha256Digests.length);
+
+ return input.success(pkg.addUsesStaticLibrary(lname)
+ .addUsesStaticLibraryVersion(version)
+ .addUsesStaticLibraryCertDigests(certSha256Digests));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name);
+ boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true);
+
+ if (lname != null) {
+ lname = lname.intern();
+ if (req) {
+ // Upgrade to treat as stronger constraint
+ pkg.addUsesLibrary(lname)
+ .removeUsesOptionalLibrary(lname);
+ } else {
+ // Ignore if someone already defined as required
+ if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
+ pkg.addUsesOptionalLibrary(lname);
+ }
+ }
+ }
+
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser, String[] separateProcesses, int flags)
+ throws IOException, XmlPullParserException {
+ ParseResult<ArrayMap<String, ParsedProcess>> result =
+ ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags,
+ input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ return input.success(pkg.setProcesses(result.getResult()));
+ }
+
+ @NonNull
+ private static ParseResult<ParsingPackage> parseProfileable(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable);
+ try {
+ return input.success(pkg.setProfileableByShell(pkg.isProfileableByShell()
+ || bool(false, R.styleable.AndroidManifestProfileable_shell, sa)));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input,
+ Resources resources, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ String[] certSha256Digests = EmptyArray.STRING;
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final String nodeName = parser.getName();
+ if (nodeName.equals("additional-certificate")) {
+ TypedArray sa = resources.obtainAttributes(parser,
+ R.styleable.AndroidManifestAdditionalCertificate);
+ try {
+ String certSha256Digest = sa.getNonResourceString(
+ R.styleable.AndroidManifestAdditionalCertificate_certDigest);
+
+ if (TextUtils.isEmpty(certSha256Digest)) {
+ return input.error("Bad additional-certificate declaration with empty"
+ + " certDigest:" + certSha256Digest);
+ }
+
+
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+ certSha256Digests = ArrayUtils.appendElement(String.class,
+ certSha256Digests, certSha256Digest);
+ } finally {
+ sa.recycle();
+ }
+ }
+ }
+
+ return input.success(certSha256Digests);
+ }
+
+ /**
+ * Generate activity object that forwards user to App Details page automatically.
+ * This activity should be invisible to user and user should not know or see it.
+ */
+ @NonNull
+ private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input,
+ ParsingPackage pkg) {
+ String packageName = pkg.getPackageName();
+ ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
+ packageName, packageName, ":app_details", input);
+
+ String taskAffinity;
+ if (taskAffinityResult.isSuccess()) {
+ taskAffinity = taskAffinityResult.getResult();
+ } else {
+ // Backwards-compat, do not fail
+ taskAffinity = null;
+ taskAffinityResult.ignoreError();
+ }
+
+ // Build custom App Details activity info instead of parsing it from xml
+ return input.success(ParsedActivity.makeAppDetailsActivity(packageName,
+ pkg.getProcessName(), pkg.getUiOptions(), taskAffinity,
+ pkg.isBaseHardwareAccelerated()));
+ }
+
+ /**
+ * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
+ */
+ private static boolean hasDomainURLs(ParsingPackage pkg) {
+ final List<ParsedActivity> activities = pkg.getActivities();
+ final int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ ParsedActivity activity = activities.get(index);
+ List<ParsedIntentInfo> filters = activity.getIntents();
+ final int filtersSize = filters.size();
+ for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) {
+ ParsedIntentInfo aii = filters.get(filtersIndex);
+ if (!aii.hasAction(Intent.ACTION_VIEW)) continue;
+ if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
+ if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+ aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sets the max aspect ratio of every child activity that doesn't already have an aspect
+ * ratio set.
+ */
+ private static void setMaxAspectRatio(ParsingPackage pkg) {
+ // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
+ // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
+ float maxAspectRatio = pkg.getTargetSdkVersion() < O
+ ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+
+ float packageMaxAspectRatio = pkg.getMaxAspectRatio();
+ if (packageMaxAspectRatio != 0) {
+ // Use the application max aspect ration as default if set.
+ maxAspectRatio = packageMaxAspectRatio;
+ } else {
+ Bundle appMetaData = pkg.getMetaData();
+ if (appMetaData != null && appMetaData.containsKey(
+ PackageParser.METADATA_MAX_ASPECT_RATIO)) {
+ maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
+ maxAspectRatio);
+ }
+ }
+
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ ParsedActivity activity = activities.get(index);
+ // If the max aspect ratio for the activity has already been set, skip.
+ if (activity.getMaxAspectRatio() != null) {
+ continue;
+ }
+
+ // By default we prefer to use a values defined on the activity directly than values
+ // defined on the application. We do not check the styled attributes on the activity
+ // as it would have already been set when we processed the activity. We wait to
+ // process the meta data here since this method is called at the end of processing
+ // the application and all meta data is guaranteed.
+ final float activityAspectRatio = activity.getMetaData() != null
+ ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
+ maxAspectRatio)
+ : maxAspectRatio;
+
+ activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
+ }
+ }
+
+ /**
+ * Sets the min aspect ratio of every child activity that doesn't already have an aspect
+ * ratio set.
+ */
+ private void setMinAspectRatio(ParsingPackage pkg) {
+ final float minAspectRatio;
+ float packageMinAspectRatio = pkg.getMinAspectRatio();
+ if (packageMinAspectRatio != 0) {
+ // Use the application max aspect ration as default if set.
+ minAspectRatio = packageMinAspectRatio;
+ } else {
+ // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater.
+ // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD,
+ // except for watches which always supported 1:1.
+ minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q
+ ? 0
+ : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH))
+ ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH
+ : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO;
+ }
+
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ ParsedActivity activity = activities.get(index);
+ if (activity.getMinAspectRatio() == null) {
+ activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio);
+ }
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg,
+ Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay);
+ try {
+ String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage);
+ int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa);
+
+ if (target == null) {
+ return input.error("<overlay> does not specify a target package");
+ } else if (priority < 0 || priority > 9999) {
+ return input.error("<overlay> priority must be between 0 and 9999");
+ }
+
+ // check to see if overlay should be excluded based on system property condition
+ String propName = sa.getString(
+ R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName);
+ String propValue = sa.getString(
+ R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
+ if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
+ Slog.i(TAG, "Skipping target and overlay pair " + target + " and "
+ + pkg.getBaseCodePath()
+ + ": overlay ignored due to required system property: "
+ + propName + " with value: " + propValue);
+ return input.error("Skipping target and overlay pair " + target + " and "
+ + pkg.getBaseCodePath()
+ + ": overlay ignored due to required system property: "
+ + propName + " with value: " + propValue);
+ }
+
+ return input.success(pkg.setOverlay(true)
+ .setOverlayTarget(target)
+ .setOverlayPriority(priority)
+ .setOverlayTargetName(
+ sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName))
+ .setOverlayCategory(
+ sa.getString(R.styleable.AndroidManifestResourceOverlay_category))
+ .setOverlayIsStatic(
+ bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa)));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast);
+ try {
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa);
+ if (name != null) {
+ pkg.addProtectedBroadcast(name);
+ }
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens);
+ try {
+ int requiresSmallestWidthDp = anInt(0,
+ R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa);
+ int compatibleWidthLimitDp = anInt(0,
+ R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa);
+ int largestWidthLimitDp = anInt(0,
+ R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa);
+
+ // This is a trick to get a boolean and still able to detect
+ // if a value was actually set.
+ return input.success(pkg
+ .setSupportsSmallScreens(
+ anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa))
+ .setSupportsNormalScreens(
+ anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa))
+ .setSupportsLargeScreens(
+ anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa))
+ .setSupportsExtraLargeScreens(
+ anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa))
+ .setResizeable(
+ anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa))
+ .setAnyDensity(
+ anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa))
+ .setRequiresSmallestWidthDp(requiresSmallestWidthDp)
+ .setCompatibleWidthLimitDp(compatibleWidthLimitDp)
+ .setLargestWidthLimitDp(largestWidthLimitDp));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
+ pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ return input.success(pkg.addInstrumentation(result.getResult()));
+ }
+
+ private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+ try {
+ String orig = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestOriginalPackage_name,
+ 0);
+ if (!pkg.getPackageName().equals(orig)) {
+ if (pkg.getOriginalPackages().isEmpty()) {
+ pkg.setRealPackage(pkg.getPackageName());
+ }
+ pkg.addOriginalPackage(orig);
+ }
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage);
+ try {
+ String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa);
+ if (name != null) {
+ pkg.addAdoptPermission(name);
+ }
+ return input.success(pkg);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static void convertNewPermissions(ParsingPackage pkg) {
+ final int NP = PackageParser.NEW_PERMISSIONS.length;
+ StringBuilder newPermsMsg = null;
+ for (int ip = 0; ip < NP; ip++) {
+ final PackageParser.NewPermissionInfo npi
+ = PackageParser.NEW_PERMISSIONS[ip];
+ if (pkg.getTargetSdkVersion() >= npi.sdkVersion) {
+ break;
+ }
+ if (!pkg.getRequestedPermissions().contains(npi.name)) {
+ if (newPermsMsg == null) {
+ newPermsMsg = new StringBuilder(128);
+ newPermsMsg.append(pkg.getPackageName());
+ newPermsMsg.append(": compat added ");
+ } else {
+ newPermsMsg.append(' ');
+ }
+ newPermsMsg.append(npi.name);
+ pkg.addRequestedPermission(npi.name)
+ .addImplicitPermission(npi.name);
+ }
+ }
+ if (newPermsMsg != null) {
+ Slog.i(TAG, newPermsMsg.toString());
+ }
+ }
+
+ private static void convertSplitPermissions(ParsingPackage pkg) {
+ List<SplitPermissionInfoParcelable> splitPermissions;
+
+ try {
+ splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ final int listSize = splitPermissions.size();
+ for (int is = 0; is < listSize; is++) {
+ final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
+ List<String> requestedPermissions = pkg.getRequestedPermissions();
+ if (pkg.getTargetSdkVersion() >= spi.getTargetSdk()
+ || !requestedPermissions.contains(spi.getSplitPermission())) {
+ continue;
+ }
+ final List<String> newPerms = spi.getNewPermissions();
+ for (int in = 0; in < newPerms.size(); in++) {
+ final String perm = newPerms.get(in);
+ if (!requestedPermissions.contains(perm)) {
+ pkg.addRequestedPermission(perm)
+ .addImplicitPermission(perm);
+ }
+ }
+ }
+ }
+
+ private static boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {
+ if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
+ if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
+ // malformed condition - incomplete
+ Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName
+ + "=" + propValue + "' - require both requiredSystemPropertyName"
+ + " AND requiredSystemPropertyValue to be specified.");
+ return false;
+ }
+ // no valid condition set - so no exclusion criteria, overlay will be included.
+ return true;
+ }
+
+ // check property value - make sure it is both set and equal to expected value
+ final String currValue = SystemProperties.get(propName);
+ return (currValue != null && currValue.equals(propValue));
+ }
+
+ /**
+ * This is a pre-density application which will get scaled - instead of being pixel perfect.
+ * This type of application is not resizable.
+ *
+ * @param pkg The package which needs to be marked as unresizable.
+ */
+ private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) {
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ ParsedActivity activity = activities.get(index);
+ activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE);
+ }
+ }
+
+ private static ParseResult validateName(ParseInput input, String name, boolean requireSeparator,
+ boolean requireFilename) {
+ final int N = name.length();
+ boolean hasSep = false;
+ boolean front = true;
+ for (int i = 0; i < N; i++) {
+ final char c = name.charAt(i);
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ front = false;
+ continue;
+ }
+ if (!front) {
+ if ((c >= '0' && c <= '9') || c == '_') {
+ continue;
+ }
+ }
+ if (c == '.') {
+ hasSep = true;
+ front = true;
+ continue;
+ }
+ return input.error("bad character '" + c + "'");
+ }
+ if (requireFilename && !FileUtils.isValidExtFilename(name)) {
+ return input.error("Invalid filename");
+ }
+ return hasSep || !requireSeparator
+ ? input.success(null)
+ : input.error("must have at least one '.' separator");
+ }
+
+ public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, Bundle data, ParseInput input) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
+ try {
+ if (data == null) {
+ data = new Bundle();
+ }
+
+ String name = TextUtils.safeIntern(
+ nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
+ if (name == null) {
+ return input.error("<meta-data> requires an android:name attribute");
+ }
+
+ TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
+ if (v != null && v.resourceId != 0) {
+ //Slog.i(TAG, "Meta data ref " + name + ": " + v);
+ data.putInt(name, v.resourceId);
+ } else {
+ v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
+ //Slog.i(TAG, "Meta data " + name + ": " + v);
+ if (v != null) {
+ if (v.type == TypedValue.TYPE_STRING) {
+ CharSequence cs = v.coerceToString();
+ data.putString(name, cs != null ? cs.toString() : null);
+ } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+ data.putBoolean(name, v.data != 0);
+ } else if (v.type >= TypedValue.TYPE_FIRST_INT
+ && v.type <= TypedValue.TYPE_LAST_INT) {
+ data.putInt(name, v.data);
+ } else if (v.type == TypedValue.TYPE_FLOAT) {
+ data.putFloat(name, v.getFloat());
+ } else {
+ if (!PackageParser.RIGID_PARSER) {
+ Slog.w(TAG,
+ "<meta-data> only supports string, integer, float, color, "
+ + "boolean, and resource reference types: "
+ + parser.getName() + " at "
+ + pkg.getBaseCodePath() + " "
+ + parser.getPositionDescription());
+ } else {
+ return input.error("<meta-data> only supports string, integer, float, "
+ + "color, boolean, and resource reference types");
+ }
+ }
+ } else {
+ return input.error("<meta-data> requires an android:value "
+ + "or android:resource attribute");
+ }
+ }
+ return input.success(data);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ /**
+ * Collect certificates from all the APKs described in the given package. Also asserts that
+ * all APK contents are signed correctly and consistently.
+ */
+ public static SigningDetails collectCertificates(ParsingPackageRead pkg, boolean skipVerify)
+ throws PackageParserException {
+ SigningDetails signingDetails = SigningDetails.UNKNOWN;
+
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+ try {
+ signingDetails = collectCertificates(
+ pkg.getBaseCodePath(),
+ skipVerify,
+ pkg.isStaticSharedLibrary(),
+ signingDetails,
+ pkg.getTargetSdkVersion()
+ );
+
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ signingDetails = collectCertificates(
+ splitCodePaths[i],
+ skipVerify,
+ pkg.isStaticSharedLibrary(),
+ signingDetails,
+ pkg.getTargetSdkVersion()
+ );
+ }
+ }
+ return signingDetails;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+ }
+
+ public static SigningDetails collectCertificates(String baseCodePath, boolean skipVerify,
+ boolean isStaticSharedLibrary, @NonNull SigningDetails existingSigningDetails,
+ int targetSdk) throws PackageParserException {
+ int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
+ targetSdk);
+ if (isStaticSharedLibrary) {
+ // must use v2 signing scheme
+ minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2;
+ }
+ SigningDetails verified;
+ if (skipVerify) {
+ // systemDir APKs are already trusted, save time by not verifying
+ verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification(
+ baseCodePath, minSignatureScheme);
+ } else {
+ verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme);
+ }
+
+ // Verify that entries are signed consistently with the first pkg
+ // we encountered. Note that for splits, certificates may have
+ // already been populated during an earlier parse of a base APK.
+ if (existingSigningDetails == SigningDetails.UNKNOWN) {
+ return verified;
+ } else {
+ if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) {
+ throw new PackageParserException(
+ INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+ baseCodePath + " has mismatched certificates");
+ }
+
+ return existingSigningDetails;
+ }
+ }
+
+ /*
+ The following set of methods makes code easier to read by re-ordering the TypedArray methods.
+
+ The first parameter is the default, which is the most important to understand for someone
+ reading through the parsing code.
+
+ That's followed by the attribute name, which is usually irrelevant during reading because
+ it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and
+ the "setSomeValue" part is enough to communicate what the line does.
+
+ Last comes the TypedArray, which is by far the least important since each try-with-resources
+ should only have 1.
+ */
+
+ // Note there is no variant of bool without a defaultValue parameter, since explicit true/false
+ // is important to specify when adding an attribute.
+ private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) {
+ return sa.getBoolean(attribute, defaultValue);
+ }
+
+ private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) {
+ return sa.getFloat(attribute, defaultValue);
+ }
+
+ private static float aFloat(@StyleableRes int attribute, TypedArray sa) {
+ return sa.getFloat(attribute, 0f);
+ }
+
+ private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
+ return sa.getInt(attribute, defaultValue);
+ }
+
+ private static int anInt(@StyleableRes int attribute, TypedArray sa) {
+ return sa.getInt(attribute, 0);
+ }
+
+ @AnyRes
+ private static int resId(@StyleableRes int attribute, TypedArray sa) {
+ return sa.getResourceId(attribute, 0);
+ }
+
+ private static String string(@StyleableRes int attribute, TypedArray sa) {
+ return sa.getString(attribute);
+ }
+
+ private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute,
+ TypedArray sa) {
+ return sa.getNonConfigurationString(attribute, allowedChangingConfigs);
+ }
+
+ private static String nonResString(@StyleableRes int index, TypedArray sa) {
+ return sa.getNonResourceString(index);
+ }
+
+ /**
+ * Callback interface for retrieving information that may be needed while parsing
+ * a package.
+ */
+ public interface Callback {
+ boolean hasFeature(String feature);
+
+ ParsingPackage startParsingPackage(String packageName, String baseCodePath, String codePath,
+ TypedArray manifestArray, boolean isCoreApp);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/ParsingUtils.java b/core/java/android/content/pm/parsing/ParsingUtils.java
new file mode 100644
index 0000000..ba61de1
--- /dev/null
+++ b/core/java/android/content/pm/parsing/ParsingUtils.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.parsing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.XmlResourceParser;
+import android.util.Slog;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide **/
+public class ParsingUtils {
+
+ // TODO(b/135203078): Consolidate log tags
+ public static final String TAG = "PackageParsing";
+
+ @Nullable
+ public static String buildClassName(String pkg, CharSequence clsSeq) {
+ if (clsSeq == null || clsSeq.length() <= 0) {
+ return null;
+ }
+ String cls = clsSeq.toString();
+ char c = cls.charAt(0);
+ if (c == '.') {
+ return pkg + cls;
+ }
+ if (cls.indexOf('.') < 0) {
+ StringBuilder b = new StringBuilder(pkg);
+ b.append('.');
+ b.append(cls);
+ return b.toString();
+ }
+ return cls;
+ }
+
+ @NonNull
+ public static ParseResult unknownTag(String parentTag, ParsingPackage pkg,
+ XmlResourceParser parser, ParseInput input) throws IOException, XmlPullParserException {
+ if (PackageParser.RIGID_PARSER) {
+ return input.error("Bad element under " + parentTag + ": " + parser.getName());
+ }
+ Slog.w(TAG, "Unknown element under " + parentTag + ": "
+ + parser.getName() + " at " + pkg.getBaseCodePath() + " "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ return input.success(null); // Type doesn't matter
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
new file mode 100644
index 0000000..c4caedc
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.AttrRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ComponentParseUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
+ return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
+ || intentInfo.hasAction(Intent.ACTION_SEND)
+ || intentInfo.hasAction(Intent.ACTION_SENDTO)
+ || intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE);
+ }
+
+ static <Component extends ParsedComponent> ParseResult<Component> parseAllMetaData(
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
+ Component component, ParseInput input) throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final ParseResult result;
+ if ("meta-data".equals(parser.getName())) {
+ result = ParsedComponentUtils.addMetaData(component, pkg, res, parser, input);
+ } else {
+ result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ return input.success(component);
+ }
+
+ @NonNull
+ public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
+ CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
+ if ((flags & PackageParser.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
+ procSeq)) {
+ return input.success(defProc != null ? defProc : pkg);
+ }
+ if (separateProcesses != null) {
+ for (int i = separateProcesses.length - 1; i >= 0; i--) {
+ String sp = separateProcesses[i];
+ if (sp.equals(pkg) || sp.equals(defProc) || sp.contentEquals(procSeq)) {
+ return input.success(pkg);
+ }
+ }
+ }
+ if (procSeq == null || procSeq.length() <= 0) {
+ return input.success(defProc);
+ }
+
+ ParseResult<String> nameResult = ComponentParseUtils.buildCompoundName(pkg, procSeq,
+ "process", input);
+ return input.success(TextUtils.safeIntern(nameResult.getResult()));
+ }
+
+ @NonNull
+ public static ParseResult<String> buildTaskAffinityName(String pkg, String defProc,
+ CharSequence procSeq, ParseInput input) {
+ if (procSeq == null) {
+ return input.success(defProc);
+ }
+ if (procSeq.length() <= 0) {
+ return input.success(null);
+ }
+ return buildCompoundName(pkg, procSeq, "taskAffinity", input);
+ }
+
+ public static ParseResult<String> buildCompoundName(String pkg, CharSequence procSeq,
+ String type, ParseInput input) {
+ String proc = procSeq.toString();
+ char c = proc.charAt(0);
+ if (pkg != null && c == ':') {
+ if (proc.length() < 2) {
+ return input.error("Bad " + type + " name " + proc + " in package " + pkg
+ + ": must be at least two characters");
+ }
+ String subName = proc.substring(1);
+ String nameError = PackageParser.validateName(subName, false, false);
+ if (nameError != null) {
+ return input.error("Invalid " + type + " name " + proc + " in package " + pkg
+ + ": " + nameError);
+ }
+ return input.success(pkg + proc);
+ }
+ String nameError = PackageParser.validateName(proc, true, false);
+ if (nameError != null && !"system".equals(proc)) {
+ return input.error("Invalid " + type + " name " + proc + " in package " + pkg
+ + ": " + nameError);
+ }
+ return input.success(proc);
+ }
+
+ public static int flag(int flag, @AttrRes int attribute, TypedArray typedArray) {
+ return typedArray.getBoolean(attribute, false) ? flag : 0;
+ }
+
+ public static int flag(int flag, @AttrRes int attribute, boolean defaultValue,
+ TypedArray typedArray) {
+ return typedArray.getBoolean(attribute, defaultValue) ? flag : 0;
+ }
+
+ /**
+ * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
+ */
+ @Nullable
+ public static CharSequence getNonLocalizedLabel(
+ ParsedComponent component) {
+ return component.nonLocalizedLabel;
+ }
+
+ /**
+ * This is not state aware. Avoid and access through PackageInfoUtils in the system server.
+ *
+ * This is a method of the utility class to discourage use.
+ */
+ public static int getIcon(ParsedComponent component) {
+ return component.icon;
+ }
+
+ public static boolean isMatch(PackageUserState state, boolean isSystem,
+ boolean isPackageEnabled, ParsedMainComponent component, int flags) {
+ return state.isMatch(isSystem, isPackageEnabled, component.isEnabled(),
+ component.isDirectBootAware(), component.getName(), flags);
+ }
+
+ public static boolean isEnabled(PackageUserState state, boolean isPackageEnabled,
+ ParsedMainComponent parsedComponent, int flags) {
+ return state.isEnabled(isPackageEnabled, parsedComponent.isEnabled(),
+ parsedComponent.getName(), flags);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
new file mode 100644
index 0000000..5495c22
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
+
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsingPackageImpl;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+public class ParsedActivity extends ParsedMainComponent {
+
+ int theme;
+ int uiOptions;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String targetActivity;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String parentActivityName;
+ @Nullable
+ String taskAffinity;
+ int privateFlags;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String permission;
+
+ int launchMode;
+ int documentLaunchMode;
+ int maxRecents;
+ int configChanges;
+ int softInputMode;
+ int persistableMode;
+ int lockTaskLaunchMode;
+
+ int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+ @Nullable
+ private Float maxAspectRatio;
+
+ @Nullable
+ private Float minAspectRatio;
+
+ @Nullable
+ String requestedVrComponent;
+ int rotationAnimation = -1;
+ int colorMode;
+
+ boolean preferMinimalPostProcessing;
+
+ @Nullable
+ ActivityInfo.WindowLayout windowLayout;
+
+ public ParsedActivity(ParsedActivity other) {
+ super(other);
+ this.theme = other.theme;
+ this.uiOptions = other.uiOptions;
+ this.targetActivity = other.targetActivity;
+ this.parentActivityName = other.parentActivityName;
+ this.taskAffinity = other.taskAffinity;
+ this.privateFlags = other.privateFlags;
+ this.permission = other.permission;
+ this.launchMode = other.launchMode;
+ this.documentLaunchMode = other.documentLaunchMode;
+ this.maxRecents = other.maxRecents;
+ this.configChanges = other.configChanges;
+ this.softInputMode = other.softInputMode;
+ this.persistableMode = other.persistableMode;
+ this.lockTaskLaunchMode = other.lockTaskLaunchMode;
+ this.screenOrientation = other.screenOrientation;
+ this.resizeMode = other.resizeMode;
+ this.maxAspectRatio = other.maxAspectRatio;
+ this.minAspectRatio = other.minAspectRatio;
+ this.requestedVrComponent = other.requestedVrComponent;
+ this.rotationAnimation = other.rotationAnimation;
+ this.colorMode = other.colorMode;
+ this.windowLayout = other.windowLayout;
+ }
+
+ /**
+ * Generate activity object that forwards user to App Details page automatically.
+ * This activity should be invisible to user and user should not know or see it.
+ */
+ public static ParsedActivity makeAppDetailsActivity(String packageName, String processName,
+ int uiOptions, String taskAffinity, boolean hardwareAccelerated) {
+ ParsedActivity activity = new ParsedActivity();
+ activity.setPackageName(packageName);
+ activity.theme = android.R.style.Theme_NoDisplay;
+ activity.exported = true;
+ activity.setName(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
+ activity.setProcessName(processName);
+ activity.uiOptions = uiOptions;
+ activity.taskAffinity = taskAffinity;
+ activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
+ activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
+ activity.configChanges = PackageParser.getActivityConfigChanges(0, 0);
+ activity.softInputMode = 0;
+ activity.persistableMode = ActivityInfo.PERSIST_NEVER;
+ activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ activity.resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+ activity.lockTaskLaunchMode = 0;
+ activity.setDirectBootAware(false);
+ activity.rotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
+ activity.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+ activity.preferMinimalPostProcessing = ActivityInfo.MINIMAL_POST_PROCESSING_DEFAULT;
+ if (hardwareAccelerated) {
+ activity.setFlags(activity.getFlags() | ActivityInfo.FLAG_HARDWARE_ACCELERATED);
+ }
+ return activity;
+ }
+
+ static ParsedActivity makeAlias(String targetActivityName, ParsedActivity target) {
+ ParsedActivity alias = new ParsedActivity();
+ alias.setPackageName(target.getPackageName());
+ alias.setTargetActivity(targetActivityName);
+ alias.configChanges = target.configChanges;
+ alias.flags = target.flags;
+ alias.privateFlags = target.privateFlags;
+ alias.icon = target.icon;
+ alias.logo = target.logo;
+ alias.banner = target.banner;
+ alias.labelRes = target.labelRes;
+ alias.nonLocalizedLabel = target.nonLocalizedLabel;
+ alias.launchMode = target.launchMode;
+ alias.lockTaskLaunchMode = target.lockTaskLaunchMode;
+ alias.descriptionRes = target.descriptionRes;
+ alias.screenOrientation = target.screenOrientation;
+ alias.taskAffinity = target.taskAffinity;
+ alias.theme = target.theme;
+ alias.softInputMode = target.softInputMode;
+ alias.uiOptions = target.uiOptions;
+ alias.parentActivityName = target.parentActivityName;
+ alias.maxRecents = target.maxRecents;
+ alias.windowLayout = target.windowLayout;
+ alias.resizeMode = target.resizeMode;
+ alias.maxAspectRatio = target.maxAspectRatio;
+ alias.minAspectRatio = target.minAspectRatio;
+ alias.requestedVrComponent = target.requestedVrComponent;
+ alias.directBootAware = target.directBootAware;
+ alias.setProcessName(target.getProcessName());
+ return alias;
+
+ // Not all attributes from the target ParsedActivity are copied to the alias.
+ // Careful when adding an attribute and determine whether or not it should be copied.
+// alias.enabled = target.enabled;
+// alias.exported = target.exported;
+// alias.permission = target.permission;
+// alias.splitName = target.splitName;
+// alias.documentLaunchMode = target.documentLaunchMode;
+// alias.persistableMode = target.persistableMode;
+// alias.rotationAnimation = target.rotationAnimation;
+// alias.colorMode = target.colorMode;
+// alias.intents.addAll(target.intents);
+// alias.order = target.order;
+// alias.metaData = target.metaData;
+ }
+
+ public ParsedActivity setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
+ if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
+ || resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+ // Resizeable activities can be put in any aspect ratio.
+ return this;
+ }
+
+ if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+ // Ignore any value lesser than 1.0.
+ return this;
+ }
+
+ this.maxAspectRatio = maxAspectRatio;
+ return this;
+ }
+
+ public ParsedActivity setMinAspectRatio(int resizeMode, float minAspectRatio) {
+ if (resizeMode == RESIZE_MODE_RESIZEABLE
+ || resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+ // Resizeable activities can be put in any aspect ratio.
+ return this;
+ }
+
+ if (minAspectRatio < 1.0f && minAspectRatio != 0) {
+ // Ignore any value lesser than 1.0.
+ return this;
+ }
+
+ this.minAspectRatio = minAspectRatio;
+ return this;
+ }
+
+ public ParsedActivity setFlags(int flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ public ParsedActivity setResizeMode(int resizeMode) {
+ this.resizeMode = resizeMode;
+ return this;
+ }
+
+ public ParsedActivity setTargetActivity(String targetActivity) {
+ this.targetActivity = TextUtils.safeIntern(targetActivity);
+ return this;
+ }
+
+ public ParsedActivity setParentActivity(String parentActivity) {
+ this.parentActivityName = TextUtils.safeIntern(parentActivity);
+ return this;
+ }
+
+ public ParsedActivity setPermission(String permission) {
+ // Empty string must be converted to null
+ this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+ return this;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Activity{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ ComponentName.appendShortString(sb, getPackageName(), getName());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.theme);
+ dest.writeInt(this.uiOptions);
+ sForString.parcel(this.targetActivity, dest, flags);
+ sForString.parcel(this.parentActivityName, dest, flags);
+ dest.writeString(this.taskAffinity);
+ dest.writeInt(this.privateFlags);
+ sForString.parcel(this.permission, dest, flags);
+ dest.writeInt(this.launchMode);
+ dest.writeInt(this.documentLaunchMode);
+ dest.writeInt(this.maxRecents);
+ dest.writeInt(this.configChanges);
+ dest.writeInt(this.softInputMode);
+ dest.writeInt(this.persistableMode);
+ dest.writeInt(this.lockTaskLaunchMode);
+ dest.writeInt(this.screenOrientation);
+ dest.writeInt(this.resizeMode);
+ dest.writeValue(this.maxAspectRatio);
+ dest.writeValue(this.minAspectRatio);
+ dest.writeString(this.requestedVrComponent);
+ dest.writeInt(this.rotationAnimation);
+ dest.writeInt(this.colorMode);
+ dest.writeBoolean(this.preferMinimalPostProcessing);
+ dest.writeBundle(this.metaData);
+
+ if (windowLayout != null) {
+ dest.writeBoolean(true);
+ dest.writeInt(windowLayout.width);
+ dest.writeFloat(windowLayout.widthFraction);
+ dest.writeInt(windowLayout.height);
+ dest.writeFloat(windowLayout.heightFraction);
+ dest.writeInt(windowLayout.gravity);
+ dest.writeInt(windowLayout.minWidth);
+ dest.writeInt(windowLayout.minHeight);
+ } else {
+ dest.writeBoolean(false);
+ }
+ }
+
+ public ParsedActivity() {
+ }
+
+ protected ParsedActivity(Parcel in) {
+ super(in);
+ this.theme = in.readInt();
+ this.uiOptions = in.readInt();
+ this.targetActivity = sForString.unparcel(in);
+ this.parentActivityName = sForString.unparcel(in);
+ this.taskAffinity = in.readString();
+ this.privateFlags = in.readInt();
+ this.permission = sForString.unparcel(in);
+ this.launchMode = in.readInt();
+ this.documentLaunchMode = in.readInt();
+ this.maxRecents = in.readInt();
+ this.configChanges = in.readInt();
+ this.softInputMode = in.readInt();
+ this.persistableMode = in.readInt();
+ this.lockTaskLaunchMode = in.readInt();
+ this.screenOrientation = in.readInt();
+ this.resizeMode = in.readInt();
+ this.maxAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+ this.minAspectRatio = (Float) in.readValue(Float.class.getClassLoader());
+ this.requestedVrComponent = in.readString();
+ this.rotationAnimation = in.readInt();
+ this.colorMode = in.readInt();
+ this.preferMinimalPostProcessing = in.readBoolean();
+ this.metaData = in.readBundle();
+ if (in.readBoolean()) {
+ windowLayout = new ActivityInfo.WindowLayout(in);
+ }
+ }
+
+ public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
+ @Override
+ public ParsedActivity createFromParcel(Parcel source) {
+ return new ParsedActivity(source);
+ }
+
+ @Override
+ public ParsedActivity[] newArray(int size) {
+ return new ParsedActivity[size];
+ }
+ };
+
+ public int getTheme() {
+ return theme;
+ }
+
+ public int getUiOptions() {
+ return uiOptions;
+ }
+
+ @Nullable
+ public String getTargetActivity() {
+ return targetActivity;
+ }
+
+ @Nullable
+ public String getParentActivityName() {
+ return parentActivityName;
+ }
+
+ @Nullable
+ public String getTaskAffinity() {
+ return taskAffinity;
+ }
+
+ public int getPrivateFlags() {
+ return privateFlags;
+ }
+
+ @Nullable
+ public String getPermission() {
+ return permission;
+ }
+
+ public int getLaunchMode() {
+ return launchMode;
+ }
+
+ public int getDocumentLaunchMode() {
+ return documentLaunchMode;
+ }
+
+ public int getMaxRecents() {
+ return maxRecents;
+ }
+
+ public int getConfigChanges() {
+ return configChanges;
+ }
+
+ public int getSoftInputMode() {
+ return softInputMode;
+ }
+
+ public int getPersistableMode() {
+ return persistableMode;
+ }
+
+ public int getLockTaskLaunchMode() {
+ return lockTaskLaunchMode;
+ }
+
+ public int getScreenOrientation() {
+ return screenOrientation;
+ }
+
+ public int getResizeMode() {
+ return resizeMode;
+ }
+
+ @Nullable
+ public Float getMaxAspectRatio() {
+ return maxAspectRatio;
+ }
+
+ @Nullable
+ public Float getMinAspectRatio() {
+ return minAspectRatio;
+ }
+
+ @Nullable
+ public String getRequestedVrComponent() {
+ return requestedVrComponent;
+ }
+
+ public int getRotationAnimation() {
+ return rotationAnimation;
+ }
+
+ public int getColorMode() {
+ return colorMode;
+ }
+
+ public boolean isPreferMinimalPostProcessing() {
+ return preferMinimalPostProcessing;
+ }
+
+ @Nullable
+ public ActivityInfo.WindowLayout getWindowLayout() {
+ return windowLayout;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
new file mode 100644
index 0000000..555cdd0
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static android.content.pm.parsing.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.app.ActivityTaskManager;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageParser;
+
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedActivityUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+ boolean useRoundIcon, ParseInput input)
+ throws XmlPullParserException, IOException {
+ final String packageName = pkg.getPackageName();
+ final ParsedActivity
+ activity = new ParsedActivity();
+
+ boolean receiver = "receiver".equals(parser.getName());
+ String tag = "<" + parser.getName() + ">";
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
+ try {
+ ParseResult<ParsedActivity> result =
+ ParsedMainComponentUtils.parseMainComponent(
+ activity, tag, separateProcesses,
+ pkg, sa, flags, useRoundIcon, input,
+ R.styleable.AndroidManifestActivity_banner,
+ R.styleable.AndroidManifestActivity_description,
+ R.styleable.AndroidManifestActivity_directBootAware,
+ R.styleable.AndroidManifestActivity_enabled,
+ R.styleable.AndroidManifestActivity_icon,
+ R.styleable.AndroidManifestActivity_label,
+ R.styleable.AndroidManifestActivity_logo,
+ R.styleable.AndroidManifestActivity_name,
+ R.styleable.AndroidManifestActivity_process,
+ R.styleable.AndroidManifestActivity_roundIcon,
+ R.styleable.AndroidManifestActivity_splitName);
+ if (result.isError()) {
+ return result;
+ }
+
+ if (receiver && pkg.isCantSaveState()) {
+ // A heavy-weight application can not have receivers in its main process
+ if (Objects.equals(activity.getProcessName(), packageName)) {
+ return input.error("Heavy-weight applications can not have receivers "
+ + "in main process");
+ }
+ }
+
+ // The following section has formatting off to make it easier to read the flags.
+ // Multi-lining them to fit within the column restriction makes it hard to tell what
+ // field is assigned where.
+ // @formatter:off
+ activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
+ activity.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions());
+
+ activity.flags |= flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa)
+ | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa)
+ | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa)
+ | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa)
+ | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa)
+ | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa)
+ | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa)
+ | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa)
+ | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa)
+ | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa)
+ | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa)
+ | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa)
+ | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa);
+
+ if (!receiver) {
+ activity.flags |= flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa)
+ | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa)
+ | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa)
+ | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa)
+ | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa)
+ | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa)
+ | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
+ | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
+ | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa);
+
+ activity.privateFlags |= flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa);
+
+ activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT);
+ activity.preferMinimalPostProcessing = sa.getBoolean(R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, ActivityInfo.MINIMAL_POST_PROCESSING_DEFAULT);
+ activity.documentLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE);
+ activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
+ activity.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
+ activity.maxRecents = sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic());
+ activity.persistableMode = sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY);
+ activity.requestedVrComponent = sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
+ activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED);
+ activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
+
+ activity.configChanges = PackageParser.getActivityConfigChanges(
+ sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
+ sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
+
+ int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
+ int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
+ activity.screenOrientation = screenOrientation;
+ activity.resizeMode = resizeMode;
+
+ if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
+ && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
+ == TypedValue.TYPE_FLOAT) {
+ activity.setMaxAspectRatio(resizeMode,
+ sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
+ 0 /*default*/));
+ }
+
+ if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
+ && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
+ == TypedValue.TYPE_FLOAT) {
+ activity.setMinAspectRatio(resizeMode,
+ sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
+ 0 /*default*/));
+ }
+ } else {
+ activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ activity.configChanges = 0;
+ activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa);
+ }
+ // @formatter:on
+
+ String taskAffinity = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestActivity_taskAffinity,
+ Configuration.NATIVE_CONFIG_VERSION);
+
+ ParseResult<String> affinityNameResult = ComponentParseUtils.buildTaskAffinityName(
+ packageName, pkg.getTaskAffinity(), taskAffinity, input);
+ if (affinityNameResult.isSuccess()) {
+ activity.taskAffinity = affinityNameResult.getResult();
+ } else {
+ // Backwards-compat, ignore error
+ affinityNameResult.ignoreError();
+ }
+
+ boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
+ if (visibleToEphemeral) {
+ activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ pkg.setVisibleToInstantApps(true);
+ }
+
+ return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
+ false /*isAlias*/, visibleToEphemeral, input,
+ R.styleable.AndroidManifestActivity_parentActivityName,
+ R.styleable.AndroidManifestActivity_permission,
+ R.styleable.AndroidManifestActivity_exported
+ );
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
+ public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivityAlias);
+ try {
+ String targetActivity = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestActivityAlias_targetActivity,
+ Configuration.NATIVE_CONFIG_VERSION);
+ if (targetActivity == null) {
+ return input.error("<activity-alias> does not specify android:targetActivity");
+ }
+
+ String packageName = pkg.getPackageName();
+ targetActivity = ParsingUtils.buildClassName(packageName, targetActivity);
+ if (targetActivity == null) {
+ return input.error("Empty class name in package " + packageName);
+ }
+
+ ParsedActivity target = null;
+
+ List<ParsedActivity> activities = pkg.getActivities();
+ final int activitiesSize = ArrayUtils.size(activities);
+ for (int i = 0; i < activitiesSize; i++) {
+ ParsedActivity t = activities.get(i);
+ if (targetActivity.equals(t.getName())) {
+ target = t;
+ break;
+ }
+ }
+
+ if (target == null) {
+ return input.error("<activity-alias> target activity " + targetActivity
+ + " not found in manifest with activities = "
+ + pkg.getActivities()
+ + ", parsedActivities = " + activities);
+ }
+
+ ParsedActivity activity = ParsedActivity.makeAlias(targetActivity, target);
+ String tag = "<" + parser.getName() + ">";
+
+ ParseResult<ParsedActivity> result = ParsedMainComponentUtils.parseMainComponent(
+ activity, tag, null, pkg, sa, 0, useRoundIcon, input,
+ R.styleable.AndroidManifestActivityAlias_banner,
+ R.styleable.AndroidManifestActivityAlias_description,
+ null /*directBootAwareAttr*/,
+ R.styleable.AndroidManifestActivityAlias_enabled,
+ R.styleable.AndroidManifestActivityAlias_icon,
+ R.styleable.AndroidManifestActivityAlias_label,
+ R.styleable.AndroidManifestActivityAlias_logo,
+ R.styleable.AndroidManifestActivityAlias_name,
+ null /*processAttr*/,
+ R.styleable.AndroidManifestActivityAlias_roundIcon,
+ null /*splitNameAttr*/);
+ if (result.isError()) {
+ return result;
+ }
+
+ // TODO add visibleToInstantApps attribute to activity alias
+ final boolean visibleToEphemeral =
+ ((activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
+
+ return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, false /*isReceiver*/, true /*isAlias*/,
+ visibleToEphemeral, input,
+ R.styleable.AndroidManifestActivityAlias_parentActivityName,
+ R.styleable.AndroidManifestActivityAlias_permission,
+ R.styleable.AndroidManifestActivityAlias_exported);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ /**
+ * This method shares parsing logic between Activity/Receiver/alias instances, but requires
+ * passing in booleans for isReceiver/isAlias, since there's no indicator in the other
+ * parameters.
+ *
+ * They're used to filter the parsed tags and their behavior. This makes the method rather
+ * messy, but it's more maintainable than writing 3 separate methods for essentially the same
+ * type of logic.
+ */
+ @NonNull
+ private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,
+ ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
+ TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
+ ParseInput input, int parentActivityNameAttr, int permissionAttr,
+ int exportedAttr) throws IOException, XmlPullParserException {
+ String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
+ if (parentActivityName != null) {
+ String packageName = pkg.getPackageName();
+ String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);
+ if (parentClassName == null) {
+ Log.e(TAG, "Activity " + activity.getName()
+ + " specified invalid parentActivityName " + parentActivityName);
+ } else {
+ activity.setParentActivity(parentClassName);
+ }
+ }
+
+ String permission = array.getNonConfigurationString(permissionAttr, 0);
+ activity.setPermission(permission != null ? permission : pkg.getPermission());
+
+ final boolean setExported = array.hasValue(exportedAttr);
+ if (setExported) {
+ activity.exported = array.getBoolean(exportedAttr, false);
+ }
+
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final ParseResult result;
+ if (parser.getName().equals("intent-filter")) {
+ ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+ !isReceiver, visibleToEphemeral, resources, parser, input);
+ if (intentResult.isSuccess()) {
+ ParsedIntentInfo intent = intentResult.getResult();
+ if (intent != null) {
+ activity.order = Math.max(intent.getOrder(), activity.order);
+ activity.addIntent(intent);
+ if (PackageParser.LOG_UNSAFE_BROADCASTS && isReceiver
+ && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
+ int actionCount = intent.countActions();
+ for (int i = 0; i < actionCount; i++) {
+ final String action = intent.getAction(i);
+ if (action == null || !action.startsWith("android.")) {
+ continue;
+ }
+
+ if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
+ Slog.w(TAG,
+ "Broadcast " + action + " may never be delivered to "
+ + pkg.getPackageName() + " as requested at: "
+ + parser.getPositionDescription());
+ }
+ }
+ }
+ }
+ }
+ result = intentResult;
+ } else if (parser.getName().equals("meta-data")) {
+ result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
+ } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
+ ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
+ true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
+ resources, parser, input);
+ if (intentResult.isSuccess()) {
+ ParsedIntentInfo intent = intentResult.getResult();
+ if (intent != null) {
+ pkg.addPreferredActivityFilter(activity.getClassName(), intent);
+ }
+ }
+ result = intentResult;
+ } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
+ ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser,
+ input);
+ if (layoutResult.isSuccess()) {
+ activity.windowLayout = layoutResult.getResult();
+ }
+ result = layoutResult;
+ } else {
+ result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ if (!setExported) {
+ activity.exported = activity.getIntents().size() > 0;
+ }
+
+ return input.success(activity);
+ }
+
+ @NonNull
+ private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg,
+ ParsedActivity activity, boolean allowImplicitEphemeralVisibility,
+ boolean visibleToEphemeral, Resources resources, XmlResourceParser parser,
+ ParseInput input) throws IOException, XmlPullParserException {
+ ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity,
+ pkg, resources, parser, visibleToEphemeral, true /*allowGlobs*/,
+ true /*allowAutoVerify*/, allowImplicitEphemeralVisibility,
+ true /*failOnNoActions*/, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ ParsedIntentInfo intent = result.getResult();
+ if (intent != null) {
+ if (intent.isVisibleToInstantApp()) {
+ activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ }
+ if (intent.isImplicitlyVisibleToInstantApp()) {
+ activity.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
+ }
+ }
+
+ return input.success(intent);
+ }
+
+ private static int getActivityResizeMode(ParsingPackage pkg, TypedArray sa,
+ int screenOrientation) {
+ Boolean resizeableActivity = pkg.getResizeableActivity();
+
+ if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
+ || resizeableActivity != null) {
+ // Activity or app explicitly set if it is resizeable or not;
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
+ resizeableActivity != null && resizeableActivity)) {
+ return ActivityInfo.RESIZE_MODE_RESIZEABLE;
+ } else {
+ return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ }
+ }
+
+ if (pkg.isResizeableActivityViaSdkVersion()) {
+ // The activity or app didn't explicitly set the resizing option, however we want to
+ // make it resize due to the sdk version it is targeting.
+ return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+ }
+
+ // resize preference isn't set and target sdk version doesn't support resizing apps by
+ // default. For the app to be resizeable if it isn't fixed orientation or immersive.
+ if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
+ return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
+ } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
+ return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
+ } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+ return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
+ } else {
+ return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ActivityInfo.WindowLayout> parseLayout(Resources res,
+ AttributeSet attrs, ParseInput input) {
+ TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout);
+ try {
+ int width = -1;
+ float widthFraction = -1f;
+ int height = -1;
+ float heightFraction = -1f;
+ final int widthType = sw.getType(R.styleable.AndroidManifestLayout_defaultWidth);
+ if (widthType == TypedValue.TYPE_FRACTION) {
+ widthFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultWidth, 1, 1,
+ -1);
+ } else if (widthType == TypedValue.TYPE_DIMENSION) {
+ width = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultWidth,
+ -1);
+ }
+ final int heightType = sw.getType(R.styleable.AndroidManifestLayout_defaultHeight);
+ if (heightType == TypedValue.TYPE_FRACTION) {
+ heightFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultHeight, 1,
+ 1, -1);
+ } else if (heightType == TypedValue.TYPE_DIMENSION) {
+ height = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultHeight,
+ -1);
+ }
+ int gravity = sw.getInt(R.styleable.AndroidManifestLayout_gravity, Gravity.CENTER);
+ int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1);
+ int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight,
+ -1);
+ return input.success(new ActivityInfo.WindowLayout(width, widthFraction, height,
+ heightFraction, gravity, minWidth, minHeight));
+ } finally {
+ sw.recycle();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
new file mode 100644
index 0000000..098d620
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public abstract class ParsedComponent implements Parcelable {
+
+ private static ParsedIntentInfo.ListParceler sForIntentInfos = Parcelling.Cache.getOrCreate(
+ ParsedIntentInfo.ListParceler.class);
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String name;
+ int icon;
+ int labelRes;
+ @Nullable
+ CharSequence nonLocalizedLabel;
+ int logo;
+ int banner;
+ int descriptionRes;
+
+ // TODO(b/135203078): Replace flags with individual booleans, scoped by subclass
+ int flags;
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String packageName;
+
+ @Nullable
+ @DataClass.PluralOf("intent")
+ @DataClass.ParcelWith(ParsedIntentInfo.ListParceler.class)
+ private List<ParsedIntentInfo> intents;
+
+ private ComponentName componentName;
+
+ @Nullable
+ protected Bundle metaData;
+
+ ParsedComponent() {
+
+ }
+
+ @SuppressWarnings("IncompleteCopyConstructor")
+ public ParsedComponent(ParsedComponent other) {
+ this.metaData = other.metaData;
+ this.name = other.name;
+ this.icon = other.getIcon();
+ this.labelRes = other.getLabelRes();
+ this.nonLocalizedLabel = other.getNonLocalizedLabel();
+ this.logo = other.getLogo();
+ this.banner = other.getBanner();
+
+ this.descriptionRes = other.getDescriptionRes();
+
+ this.flags = other.getFlags();
+
+ this.setPackageName(other.packageName);
+ this.intents = new ArrayList<>(other.getIntents());
+ }
+
+ public void addIntent(ParsedIntentInfo intent) {
+ this.intents = CollectionUtils.add(this.intents, intent);
+ }
+
+ @NonNull
+ public List<ParsedIntentInfo> getIntents() {
+ return intents != null ? intents : Collections.emptyList();
+ }
+
+ public ParsedComponent setName(String name) {
+ this.name = TextUtils.safeIntern(name);
+ return this;
+ }
+
+ @CallSuper
+ public void setPackageName(@NonNull String packageName) {
+ this.packageName = TextUtils.safeIntern(packageName);
+ //noinspection ConstantConditions
+ this.componentName = null;
+
+ // Note: this method does not edit name (which can point to a class), because this package
+ // name change is not changing the package in code, but the identifier used by the system.
+ }
+
+ @NonNull
+ public ComponentName getComponentName() {
+ if (componentName == null) {
+ componentName = new ComponentName(getPackageName(), getName());
+ }
+ return componentName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ sForString.parcel(this.name, dest, flags);
+ dest.writeInt(this.getIcon());
+ dest.writeInt(this.getLabelRes());
+ dest.writeCharSequence(this.getNonLocalizedLabel());
+ dest.writeInt(this.getLogo());
+ dest.writeInt(this.getBanner());
+ dest.writeInt(this.getDescriptionRes());
+ dest.writeInt(this.getFlags());
+ sForString.parcel(this.packageName, dest, flags);
+ sForIntentInfos.parcel(this.getIntents(), dest, flags);
+ dest.writeBundle(this.metaData);
+ }
+
+ protected ParsedComponent(Parcel in) {
+ // We use the boot classloader for all classes that we load.
+ final ClassLoader boot = Object.class.getClassLoader();
+ //noinspection ConstantConditions
+ this.name = sForString.unparcel(in);
+ this.icon = in.readInt();
+ this.labelRes = in.readInt();
+ this.nonLocalizedLabel = in.readCharSequence();
+ this.logo = in.readInt();
+ this.banner = in.readInt();
+ this.descriptionRes = in.readInt();
+ this.flags = in.readInt();
+ //noinspection ConstantConditions
+ this.packageName = sForString.unparcel(in);
+ this.intents = sForIntentInfos.unparcel(in);
+ this.metaData = in.readBundle(boot);
+ }
+
+ @NonNull
+ public String getName() {
+ return name;
+ }
+
+ public int getIcon() {
+ return icon;
+ }
+
+ public int getLabelRes() {
+ return labelRes;
+ }
+
+ @Nullable
+ public CharSequence getNonLocalizedLabel() {
+ return nonLocalizedLabel;
+ }
+
+ public int getLogo() {
+ return logo;
+ }
+
+ public int getBanner() {
+ return banner;
+ }
+
+ public int getDescriptionRes() {
+ return descriptionRes;
+ }
+
+ public int getFlags() {
+ return flags;
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return packageName;
+ }
+
+ @Nullable
+ public Bundle getMetaData() {
+ return metaData;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
new file mode 100644
index 0000000..b37b617
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.TypedValue;
+
+import com.android.internal.annotations.VisibleForTesting;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+/** @hide */
+class ParsedComponentUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ static <Component extends ParsedComponent> ParseResult<Component> parseComponent(
+ Component component, String tag, ParsingPackage pkg, TypedArray array,
+ boolean useRoundIcon, ParseInput input, int bannerAttr,
+ @Nullable Integer descriptionAttr, int iconAttr, int labelAttr, int logoAttr,
+ int nameAttr, int roundIconAttr) {
+ String name = array.getNonConfigurationString(nameAttr, 0);
+ if (TextUtils.isEmpty(name)) {
+ return input.error(tag + " does not specify android:name");
+ }
+
+ String packageName = pkg.getPackageName();
+ String className = ParsingUtils.buildClassName(packageName, name);
+ if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
+ return input.error(tag + " invalid android:name");
+ }
+
+ //noinspection ConstantConditions; null check done above with isEmpty
+ component.setName(className);
+ component.setPackageName(packageName);
+
+ if (useRoundIcon) {
+ component.icon = array.getResourceId(roundIconAttr, 0);
+ }
+
+ if (component.icon == 0) {
+ component.icon = array.getResourceId(iconAttr, 0);
+ }
+
+ component.logo = array.getResourceId(logoAttr, 0);
+ component.banner = array.getResourceId(bannerAttr, 0);
+
+ if (descriptionAttr != null) {
+ component.descriptionRes = array.getResourceId(descriptionAttr, 0);
+ }
+
+ TypedValue v = array.peekValue(labelAttr);
+ if (v != null) {
+ component.labelRes = v.resourceId;
+ if (v.resourceId == 0) {
+ component.nonLocalizedLabel = v.coerceToString();
+ }
+ }
+
+ return input.success(component);
+ }
+
+ static ParseResult<Bundle> addMetaData(ParsedComponent component, ParsingPackage pkg,
+ Resources resources, XmlResourceParser parser, ParseInput input) {
+ ParseResult<Bundle> result = ParsingPackageUtils.parseMetaData(pkg, resources,
+ parser, component.metaData, input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ Bundle bundle = result.getResult();
+ component.metaData = bundle;
+ return input.success(bundle);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedFeature.java b/core/java/android/content/pm/parsing/component/ParsedFeature.java
new file mode 100644
index 0000000..b8a9098
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedFeature.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link android.R.styleable#AndroidManifestFeature <feature>} tag parsed from the
+ * manifest.
+ *
+ * @hide
+ */
+@DataClass(genAidl = false)
+public class ParsedFeature implements Parcelable {
+ /** Maximum length of featureId */
+ public static final int MAX_FEATURE_ID_LEN = 50;
+
+ /** Maximum amount of features per package */
+ private static final int MAX_NUM_FEATURES = 1000;
+
+ /** Id of the feature */
+ public final @NonNull String id;
+
+ /** User visible label fo the feature */
+ public final @StringRes int label;
+
+ /** Ids of previously declared features this feature inherits from */
+ public final @NonNull List<String> inheritFrom;
+
+ /**
+ * @return Is this set of features a valid combination for a single package?
+ */
+ public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
+ if (features == null) {
+ return true;
+ }
+
+ ArraySet<String> featureIds = new ArraySet<>(features.size());
+ ArraySet<String> inheritFromFeatureIds = new ArraySet<>();
+
+ int numFeatures = features.size();
+ if (numFeatures > MAX_NUM_FEATURES) {
+ return false;
+ }
+
+ for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+ boolean wasAdded = featureIds.add(features.get(featureNum).id);
+ if (!wasAdded) {
+ // feature id is not unique
+ return false;
+ }
+ }
+
+ for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+ ParsedFeature feature = features.get(featureNum);
+
+ int numInheritFrom = feature.inheritFrom.size();
+ for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+ String inheritFrom = feature.inheritFrom.get(inheritFromNum);
+
+ if (featureIds.contains(inheritFrom)) {
+ // Cannot inherit from a feature that is still defined
+ return false;
+ }
+
+ boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
+ if (!wasAdded) {
+ // inheritFrom is not unique
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @android.annotation.IntDef(prefix = "MAX_", value = {
+ MAX_FEATURE_ID_LEN,
+ MAX_NUM_FEATURES
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface Max {}
+
+ @DataClass.Generated.Member
+ public static String maxToString(@Max int value) {
+ switch (value) {
+ case MAX_FEATURE_ID_LEN:
+ return "MAX_FEATURE_ID_LEN";
+ case MAX_NUM_FEATURES:
+ return "MAX_NUM_FEATURES";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ /**
+ * Creates a new ParsedFeature.
+ *
+ * @param id
+ * Id of the feature
+ * @param label
+ * User visible label fo the feature
+ * @param inheritFrom
+ * Ids of previously declared features this feature inherits from
+ */
+ @DataClass.Generated.Member
+ public ParsedFeature(
+ @NonNull String id,
+ @StringRes int label,
+ @NonNull List<String> inheritFrom) {
+ this.id = id;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, id);
+ this.label = label;
+ com.android.internal.util.AnnotationValidations.validate(
+ StringRes.class, null, label);
+ this.inheritFrom = inheritFrom;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, inheritFrom);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeString(id);
+ dest.writeInt(label);
+ dest.writeStringList(inheritFrom);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ParsedFeature(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ String _id = in.readString();
+ int _label = in.readInt();
+ List<String> _inheritFrom = new ArrayList<>();
+ in.readStringList(_inheritFrom);
+
+ this.id = _id;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, id);
+ this.label = _label;
+ com.android.internal.util.AnnotationValidations.validate(
+ StringRes.class, null, label);
+ this.inheritFrom = _inheritFrom;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, inheritFrom);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
+ = new Parcelable.Creator<ParsedFeature>() {
+ @Override
+ public ParsedFeature[] newArray(int size) {
+ return new ParsedFeature[size];
+ }
+
+ @Override
+ public ParsedFeature createFromParcel(@NonNull Parcel in) {
+ return new ParsedFeature(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1581379861853L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedFeature.java",
+ inputSignatures = "public static final int MAX_FEATURE_ID_LEN\nprivate static final int MAX_NUM_FEATURES\npublic final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java b/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java
new file mode 100644
index 0000000..fb52801
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedFeatureUtils.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public class ParsedFeatureUtils {
+
+ @NonNull
+ public static ParseResult<ParsedFeature> parseFeature(Resources res, XmlResourceParser parser,
+ ParseInput input) throws IOException, XmlPullParserException {
+ String featureId;
+ int label;
+ List<String> inheritFrom = null;
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
+ if (sa == null) {
+ return input.error("<feature> could not be parsed");
+ }
+
+ try {
+ featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
+ 0);
+ if (featureId == null) {
+ return input.error("<featureId> does not specify android:featureId");
+ }
+ if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) {
+ return input.error("<featureId> is too long. Max length is "
+ + ParsedFeature.MAX_FEATURE_ID_LEN);
+ }
+
+ label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
+ if (label == Resources.ID_NULL) {
+ return input.error("<featureId> does not specify android:label");
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ int type;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("inherit-from")) {
+ sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
+ if (sa == null) {
+ return input.error("<inherit-from> could not be parsed");
+ }
+
+ try {
+ String inheritFromId = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);
+
+ if (inheritFrom == null) {
+ inheritFrom = new ArrayList<>();
+ }
+ inheritFrom.add(inheritFromId);
+ } finally {
+ sa.recycle();
+ }
+ } else {
+ return input.error("Bad element under <feature>: " + tagName);
+ }
+ }
+
+ if (inheritFrom == null) {
+ inheritFrom = Collections.emptyList();
+ } else {
+ ((ArrayList) inheritFrom).trimToSize();
+ }
+
+ return input.success(new ParsedFeature(featureId, label, inheritFrom));
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
new file mode 100644
index 0000000..396a145
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentation.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+public class ParsedInstrumentation extends ParsedComponent {
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String targetPackage;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String targetProcesses;
+ boolean handleProfiling;
+ boolean functionalTest;
+
+ public ParsedInstrumentation() {
+ }
+
+ public void setTargetPackage(@Nullable String targetPackage) {
+ this.targetPackage = TextUtils.safeIntern(targetPackage);
+ }
+
+ public void setTargetProcesses(@Nullable String targetProcesses) {
+ this.targetProcesses = TextUtils.safeIntern(targetProcesses);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Instrumentation{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ ComponentName.appendShortString(sb, getPackageName(), getName());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ sForString.parcel(this.targetPackage, dest, flags);
+ sForString.parcel(this.targetProcesses, dest, flags);
+ dest.writeBoolean(this.handleProfiling);
+ dest.writeBoolean(this.functionalTest);
+ }
+
+ protected ParsedInstrumentation(Parcel in) {
+ super(in);
+ this.targetPackage = sForString.unparcel(in);
+ this.targetProcesses = sForString.unparcel(in);
+ this.handleProfiling = in.readByte() != 0;
+ this.functionalTest = in.readByte() != 0;
+ }
+
+ public static final Parcelable.Creator<ParsedInstrumentation> CREATOR =
+ new Parcelable.Creator<ParsedInstrumentation>() {
+ @Override
+ public ParsedInstrumentation createFromParcel(Parcel source) {
+ return new ParsedInstrumentation(source);
+ }
+
+ @Override
+ public ParsedInstrumentation[] newArray(int size) {
+ return new ParsedInstrumentation[size];
+ }
+ };
+
+ @Nullable
+ public String getTargetPackage() {
+ return targetPackage;
+ }
+
+ @Nullable
+ public String getTargetProcesses() {
+ return targetProcesses;
+ }
+
+ public boolean isHandleProfiling() {
+ return handleProfiling;
+ }
+
+ public boolean isFunctionalTest() {
+ return functionalTest;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
new file mode 100644
index 0000000..89645fc
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedInstrumentationUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedInstrumentationUtils {
+
+ @NonNull
+ public static ParseResult<ParsedInstrumentation> parseInstrumentation(ParsingPackage pkg,
+ Resources res, XmlResourceParser parser, boolean useRoundIcon,
+ ParseInput input) throws IOException, XmlPullParserException {
+ ParsedInstrumentation
+ instrumentation = new ParsedInstrumentation();
+ String tag = "<" + parser.getName() + ">";
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
+ try {
+ ParseResult<ParsedInstrumentation> result = ParsedComponentUtils.parseComponent(
+ instrumentation, tag, pkg, sa, useRoundIcon, input,
+ R.styleable.AndroidManifestInstrumentation_banner,
+ null /*descriptionAttr*/,
+ R.styleable.AndroidManifestInstrumentation_icon,
+ R.styleable.AndroidManifestInstrumentation_label,
+ R.styleable.AndroidManifestInstrumentation_logo,
+ R.styleable.AndroidManifestInstrumentation_name,
+ R.styleable.AndroidManifestInstrumentation_roundIcon);
+ if (result.isError()) {
+ return result;
+ }
+
+ // @formatter:off
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ instrumentation.setTargetPackage(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage));
+ instrumentation.setTargetProcesses(sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetProcesses));
+ instrumentation.handleProfiling = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
+ instrumentation.functionalTest = sa.getBoolean(R.styleable.AndroidManifestInstrumentation_functionalTest, false);
+ // @formatter:on
+ } finally {
+ sa.recycle();
+ }
+
+ return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, instrumentation,
+ input);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
new file mode 100644
index 0000000..0ba92cc
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide **/
+public final class ParsedIntentInfo extends IntentFilter {
+
+ public static final Parceler PARCELER = new Parceler();
+
+ public static class Parceler implements Parcelling<ParsedIntentInfo> {
+
+ @Override
+ public void parcel(ParsedIntentInfo item, Parcel dest, int parcelFlags) {
+ item.writeIntentInfoToParcel(dest, parcelFlags);
+ }
+
+ @Override
+ public ParsedIntentInfo unparcel(Parcel source) {
+ return new ParsedIntentInfo(source);
+ }
+ }
+
+ public static class ListParceler implements Parcelling<List<ParsedIntentInfo>> {
+
+ /**
+ * <p>
+ * Implementation note: The serialized form for the intent list also contains the name
+ * of the concrete class that's stored in the list, and assumes that every element of the
+ * list is of the same type. This is very similar to the original parcelable mechanism.
+ * We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
+ * and is public API. It also declares Parcelable related methods as final which means
+ * we can't extend them. The approach of using composition instead of inheritance leads to
+ * a large set of cascading changes in the PackageManagerService, which seem undesirable.
+ *
+ * <p>
+ * <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
+ * to make sure their owner fields are consistent. See {@code fixupOwner}.
+ */
+ @Override
+ public void parcel(List<ParsedIntentInfo> item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ return;
+ }
+
+ final int size = item.size();
+ dest.writeInt(size);
+
+ for (int index = 0; index < size; index++) {
+ PARCELER.parcel(item.get(index), dest, parcelFlags);
+ }
+ }
+
+ @Override
+ public List<ParsedIntentInfo> unparcel(Parcel source) {
+ int size = source.readInt();
+ if (size == -1) {
+ return null;
+ }
+
+ if (size == 0) {
+ return new ArrayList<>(0);
+ }
+
+ final ArrayList<ParsedIntentInfo> intentsList = new ArrayList<>(size);
+ for (int i = 0; i < size; ++i) {
+ intentsList.add(PARCELER.unparcel(source));
+ }
+
+ return intentsList;
+ }
+ }
+
+ public static class StringPairListParceler implements Parcelling<List<Pair<String, ParsedIntentInfo>>> {
+
+ @Override
+ public void parcel(List<Pair<String, ParsedIntentInfo>> item, Parcel dest,
+ int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ return;
+ }
+
+ final int size = item.size();
+ dest.writeInt(size);
+
+ for (int index = 0; index < size; index++) {
+ Pair<String, ParsedIntentInfo> pair = item.get(index);
+ dest.writeString(pair.first);
+ PARCELER.parcel(pair.second, dest, parcelFlags);
+ }
+ }
+
+ @Override
+ public List<Pair<String, ParsedIntentInfo>> unparcel(Parcel source) {
+ int size = source.readInt();
+ if (size == -1) {
+ return null;
+ }
+
+ if (size == 0) {
+ return new ArrayList<>(0);
+ }
+
+ final List<Pair<String, ParsedIntentInfo>> list = new ArrayList<>(size);
+ for (int i = 0; i < size; ++i) {
+ list.add(Pair.create(source.readString(), PARCELER.unparcel(source)));
+ }
+
+ return list;
+ }
+ }
+
+ boolean hasDefault;
+ int labelRes;
+ @Nullable
+ CharSequence nonLocalizedLabel;
+ int icon;
+
+ public ParsedIntentInfo() {
+ }
+
+ public ParsedIntentInfo(Parcel in) {
+ super(in);
+ hasDefault = in.readBoolean();
+ labelRes = in.readInt();
+ nonLocalizedLabel = in.readCharSequence();
+ icon = in.readInt();
+ }
+
+ public void writeIntentInfoToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeBoolean(hasDefault);
+ dest.writeInt(labelRes);
+ dest.writeCharSequence(nonLocalizedLabel);
+ dest.writeInt(icon);
+ }
+
+ public String toString() {
+ return "ProviderIntentInfo{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + '}';
+ }
+
+ public static final Parcelable.Creator<ParsedIntentInfo> CREATOR =
+ new Parcelable.Creator<ParsedIntentInfo>() {
+ @Override
+ public ParsedIntentInfo createFromParcel(Parcel source) {
+ return new ParsedIntentInfo(source);
+ }
+
+ @Override
+ public ParsedIntentInfo[] newArray(int size) {
+ return new ParsedIntentInfo[size];
+ }
+ };
+
+ public boolean isHasDefault() {
+ return hasDefault;
+ }
+
+ public int getLabelRes() {
+ return labelRes;
+ }
+
+ @Nullable
+ public CharSequence getNonLocalizedLabel() {
+ return nonLocalizedLabel;
+ }
+
+ public int getIcon() {
+ return icon;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
new file mode 100644
index 0000000..a7b950b
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageParser;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+/** @hide */
+public class ParsedIntentInfoUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ public static ParseResult<ParsedIntentInfo> parseIntentInfo(String className,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean allowGlobs,
+ boolean allowAutoVerify, ParseInput input)
+ throws XmlPullParserException, IOException {
+ ParsedIntentInfo intentInfo = new ParsedIntentInfo();
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestIntentFilter);
+ try {
+ intentInfo.setPriority(sa.getInt(R.styleable.AndroidManifestIntentFilter_priority, 0));
+ intentInfo.setOrder(sa.getInt(R.styleable.AndroidManifestIntentFilter_order, 0));
+
+ TypedValue v = sa.peekValue(R.styleable.AndroidManifestIntentFilter_label);
+ if (v != null) {
+ intentInfo.labelRes = v.resourceId;
+ if (v.resourceId == 0) {
+ intentInfo.nonLocalizedLabel = v.coerceToString();
+ }
+ }
+
+ if (PackageParser.sUseRoundIcon) {
+ intentInfo.icon = sa.getResourceId(
+ R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
+ }
+
+ if (intentInfo.icon == 0) {
+ intentInfo.icon = sa.getResourceId(R.styleable.AndroidManifestIntentFilter_icon, 0);
+ }
+
+ if (allowAutoVerify) {
+ intentInfo.setAutoVerify(sa.getBoolean(
+ R.styleable.AndroidManifestIntentFilter_autoVerify,
+ false));
+ }
+ } finally {
+ sa.recycle();
+ }
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final ParseResult result;
+ String nodeName = parser.getName();
+ switch (nodeName) {
+ case "action": {
+ String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
+ "name");
+ if (TextUtils.isEmpty(value)) {
+ result = input.error("No value supplied for <android:name>");
+ } else {
+ intentInfo.addAction(value);
+ result = input.success(null);
+ }
+ break;
+ }
+ case "category": {
+ String value = parser.getAttributeValue(PackageParser.ANDROID_RESOURCES,
+ "name");
+ if (TextUtils.isEmpty(value)) {
+ result = input.error("No value supplied for <android:name>");
+ } else {
+ intentInfo.addCategory(value);
+ result = input.success(null);
+ }
+ break;
+ }
+ case "data":
+ result = parseData(intentInfo, res, parser, allowGlobs, input);
+ break;
+ default:
+ result = ParsingUtils.unknownTag("<intent-filter>", pkg, parser, input);
+ break;
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
+
+ if (PackageParser.DEBUG_PARSER) {
+ final StringBuilder cats = new StringBuilder("Intent d=");
+ cats.append(intentInfo.isHasDefault());
+ cats.append(", cat=");
+
+ final Iterator<String> it = intentInfo.categoriesIterator();
+ if (it != null) {
+ while (it.hasNext()) {
+ cats.append(' ');
+ cats.append(it.next());
+ }
+ }
+ Slog.d(TAG, cats.toString());
+ }
+
+ return input.success(intentInfo);
+ }
+
+ @NonNull
+ private static ParseResult<ParsedIntentInfo> parseData(ParsedIntentInfo intentInfo,
+ Resources resources, XmlResourceParser parser, boolean allowGlobs, ParseInput input) {
+ TypedArray sa = resources.obtainAttributes(parser, R.styleable.AndroidManifestData);
+ try {
+ String str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_mimeType, 0);
+ if (str != null) {
+ try {
+ intentInfo.addDataType(str);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ return input.error(e.toString());
+ }
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_mimeGroup, 0);
+ if (str != null) {
+ intentInfo.addMimeGroup(str);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_scheme, 0);
+ if (str != null) {
+ intentInfo.addDataScheme(str);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_ssp, 0);
+ if (str != null) {
+ intentInfo.addDataSchemeSpecificPart(str,
+ PatternMatcher.PATTERN_LITERAL);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_sspPrefix, 0);
+ if (str != null) {
+ intentInfo.addDataSchemeSpecificPart(str,
+ PatternMatcher.PATTERN_PREFIX);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_sspPattern, 0);
+ if (str != null) {
+ if (!allowGlobs) {
+ return input.error(
+ "sspPattern not allowed here; ssp must be literal");
+ }
+ intentInfo.addDataSchemeSpecificPart(str,
+ PatternMatcher.PATTERN_SIMPLE_GLOB);
+ }
+
+ String host = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_host, 0);
+ String port = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_port, 0);
+ if (host != null) {
+ intentInfo.addDataAuthority(host, port);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_path, 0);
+ if (str != null) {
+ intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_pathPrefix, 0);
+ if (str != null) {
+ intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_pathPattern, 0);
+ if (str != null) {
+ if (!allowGlobs) {
+ return input.error(
+ "pathPattern not allowed here; path must be literal");
+ }
+ intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ }
+
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+ if (str != null) {
+ if (!allowGlobs) {
+ return input.error(
+ "pathAdvancedPattern not allowed here; path must be literal");
+ }
+ intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+ }
+
+ return input.success(null);
+ } finally {
+ sa.recycle();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponent.java b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
new file mode 100644
index 0000000..59e9a84
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponent.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+public class ParsedMainComponent extends ParsedComponent {
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String processName;
+ boolean directBootAware;
+ boolean enabled = true;
+ boolean exported;
+ int order;
+
+ @Nullable
+ String splitName;
+
+ public ParsedMainComponent() {
+ }
+
+ public ParsedMainComponent(ParsedMainComponent other) {
+ super(other);
+ this.processName = other.processName;
+ this.directBootAware = other.directBootAware;
+ this.enabled = other.enabled;
+ this.exported = other.exported;
+ this.order = other.order;
+ this.splitName = other.splitName;
+ }
+
+ public ParsedMainComponent setProcessName(String processName) {
+ this.processName = TextUtils.safeIntern(processName);
+ return this;
+ }
+
+ public ParsedMainComponent setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
+ /**
+ * A main component's name is a class name. This makes code slightly more readable.
+ */
+ public String getClassName() {
+ return getName();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ sForString.parcel(this.processName, dest, flags);
+ dest.writeBoolean(this.directBootAware);
+ dest.writeBoolean(this.enabled);
+ dest.writeBoolean(this.exported);
+ dest.writeInt(this.order);
+ dest.writeString(this.splitName);
+ }
+
+ protected ParsedMainComponent(Parcel in) {
+ super(in);
+ this.processName = sForString.unparcel(in);
+ this.directBootAware = in.readBoolean();
+ this.enabled = in.readBoolean();
+ this.exported = in.readBoolean();
+ this.order = in.readInt();
+ this.splitName = in.readString();
+ }
+
+ public static final Parcelable.Creator<ParsedMainComponent> CREATOR =
+ new Parcelable.Creator<ParsedMainComponent>() {
+ @Override
+ public ParsedMainComponent createFromParcel(Parcel source) {
+ return new ParsedMainComponent(source);
+ }
+
+ @Override
+ public ParsedMainComponent[] newArray(int size) {
+ return new ParsedMainComponent[size];
+ }
+ };
+
+ @Nullable
+ public String getProcessName() {
+ return processName;
+ }
+
+ public boolean isDirectBootAware() {
+ return directBootAware;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public boolean isExported() {
+ return exported;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ @Nullable
+ public String getSplitName() {
+ return splitName;
+ }
+
+ public ParsedMainComponent setDirectBootAware(boolean value) {
+ directBootAware = value;
+ return this;
+ }
+
+ public ParsedMainComponent setExported(boolean value) {
+ exported = value;
+ return this;
+ }
+
+ public ParsedMainComponent setSplitName(@Nullable String value) {
+ splitName = value;
+ return this;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
new file mode 100644
index 0000000..6188f89
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedMainComponentUtils.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentFilter;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+class ParsedMainComponentUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ static <Component extends ParsedMainComponent> ParseResult<Component> parseMainComponent(
+ Component component, String tag, String[] separateProcesses, ParsingPackage pkg,
+ TypedArray array, int flags, boolean useRoundIcon, ParseInput input,
+ int bannerAttr, int descriptionAttr, @Nullable Integer directBootAwareAttr,
+ @Nullable Integer enabledAttr, int iconAttr, int labelAttr, int logoAttr, int nameAttr,
+ @Nullable Integer processAttr, int roundIconAttr, @Nullable Integer splitNameAttr) {
+ ParseResult<Component> result = ParsedComponentUtils.parseComponent(component, tag, pkg,
+ array, useRoundIcon, input, bannerAttr, descriptionAttr, iconAttr, labelAttr,
+ logoAttr, nameAttr, roundIconAttr);
+ if (result.isError()) {
+ return result;
+ }
+
+ if (directBootAwareAttr != null) {
+ component.directBootAware = array.getBoolean(directBootAwareAttr, false);
+ if (component.isDirectBootAware()) {
+ pkg.setPartiallyDirectBootAware(true);
+ }
+ }
+
+ if (enabledAttr != null) {
+ component.enabled = array.getBoolean(enabledAttr, true);
+ }
+
+ if (processAttr != null) {
+ CharSequence processName;
+ if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
+ processName = array.getNonConfigurationString(processAttr,
+ Configuration.NATIVE_CONFIG_VERSION);
+ } else {
+ // Some older apps have been seen to use a resource reference
+ // here that on older builds was ignored (with a warning). We
+ // need to continue to do this for them so they don't break.
+ processName = array.getNonResourceString(processAttr);
+ }
+
+ // Backwards-compat, ignore error
+ ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+ pkg.getPackageName(), pkg.getProcessName(), processName, flags,
+ separateProcesses, input);
+ if (processNameResult.isSuccess()) {
+ component.setProcessName(processNameResult.getResult());
+ } else {
+ // Backwards-compat, ignore error
+ processNameResult.ignoreError();
+ }
+ }
+
+ if (splitNameAttr != null) {
+ component.splitName = array.getNonConfigurationString(splitNameAttr, 0);
+ }
+
+ return input.success(component);
+ }
+
+ static ParseResult<ParsedIntentInfo> parseIntentFilter(
+ ParsedMainComponent mainComponent,
+ ParsingPackage pkg, Resources resources, XmlResourceParser parser,
+ boolean visibleToEphemeral, boolean allowGlobs, boolean allowAutoVerify,
+ boolean allowImplicitEphemeralVisibility, boolean failOnNoActions,
+ ParseInput input) throws IOException, XmlPullParserException {
+ ParseResult<ParsedIntentInfo> intentResult = ParsedIntentInfoUtils.parseIntentInfo(
+ mainComponent.getName(), pkg, resources, parser, allowGlobs,
+ allowAutoVerify, input);
+ if (intentResult.isError()) {
+ return input.error(intentResult);
+ }
+
+ ParsedIntentInfo intent = intentResult.getResult();
+ int actionCount = intent.countActions();
+ if (actionCount == 0 && failOnNoActions) {
+ Slog.w(TAG, "No actions in " + parser.getName() + " at " + pkg.getBaseCodePath() + " "
+ + parser.getPositionDescription());
+ // Backward-compat, do not actually fail
+ return input.success(null);
+ }
+
+ int intentVisibility;
+ if (visibleToEphemeral) {
+ intentVisibility = IntentFilter.VISIBILITY_EXPLICIT;
+ } else if (allowImplicitEphemeralVisibility
+ && ComponentParseUtils.isImplicitlyExposedIntent(intent)){
+ intentVisibility = IntentFilter.VISIBILITY_IMPLICIT;
+ } else {
+ intentVisibility = IntentFilter.VISIBILITY_NONE;
+ }
+ intent.setVisibilityToInstantApp(intentVisibility);
+
+ return input.success(intentResult.getResult());
+ }
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java
new file mode 100644
index 0000000..6c36ecb
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+
+import android.annotation.Nullable;
+import android.content.pm.PermissionInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide */
+public class ParsedPermission extends ParsedComponent {
+
+ @Nullable
+ String backgroundPermission;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String group;
+ int requestRes;
+ int protectionLevel;
+ boolean tree;
+ @Nullable
+ private ParsedPermissionGroup parsedPermissionGroup;
+
+ @VisibleForTesting
+ public ParsedPermission() {
+ }
+
+ public ParsedPermission(ParsedPermission other) {
+ super(other);
+ this.backgroundPermission = other.backgroundPermission;
+ this.group = other.group;
+ this.requestRes = other.requestRes;
+ this.protectionLevel = other.protectionLevel;
+ this.tree = other.tree;
+ this.parsedPermissionGroup = other.parsedPermissionGroup;
+ }
+
+ public ParsedPermission(ParsedPermission other, PermissionInfo pendingPermissionInfo,
+ String packageName, String name) {
+ this(other);
+
+ this.flags = pendingPermissionInfo.flags;
+ this.descriptionRes = pendingPermissionInfo.descriptionRes;
+
+ this.backgroundPermission = pendingPermissionInfo.backgroundPermission;
+ this.group = pendingPermissionInfo.group;
+ this.requestRes = pendingPermissionInfo.requestRes;
+ this.protectionLevel = pendingPermissionInfo.protectionLevel;
+
+ setName(name);
+ setPackageName(packageName);
+ }
+
+ public ParsedPermission setGroup(String group) {
+ this.group = TextUtils.safeIntern(group);
+ return this;
+ }
+
+ public ParsedPermission setFlags(int flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ public boolean isRuntime() {
+ return getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
+ }
+
+ public boolean isAppOp() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ }
+
+ @PermissionInfo.Protection
+ public int getProtection() {
+ return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
+ }
+
+ public int getProtectionFlags() {
+ return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
+ }
+
+ public int calculateFootprint() {
+ int size = getName().length();
+ if (getNonLocalizedLabel() != null) {
+ size += getNonLocalizedLabel().length();
+ }
+ return size;
+ }
+
+ public String toString() {
+ return "Permission{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + getName() + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(this.backgroundPermission);
+ sForString.parcel(this.group, dest, flags);
+ dest.writeInt(this.requestRes);
+ dest.writeInt(this.protectionLevel);
+ dest.writeBoolean(this.tree);
+ dest.writeParcelable(this.parsedPermissionGroup, flags);
+ }
+
+ protected ParsedPermission(Parcel in) {
+ super(in);
+ // We use the boot classloader for all classes that we load.
+ final ClassLoader boot = Object.class.getClassLoader();
+ this.backgroundPermission = in.readString();
+ this.group = sForString.unparcel(in);
+ this.requestRes = in.readInt();
+ this.protectionLevel = in.readInt();
+ this.tree = in.readBoolean();
+ this.parsedPermissionGroup = in.readParcelable(boot);
+ }
+
+ public static final Parcelable.Creator<ParsedPermission> CREATOR =
+ new Parcelable.Creator<ParsedPermission>() {
+ @Override
+ public ParsedPermission createFromParcel(Parcel source) {
+ return new ParsedPermission(source);
+ }
+
+ @Override
+ public ParsedPermission[] newArray(int size) {
+ return new ParsedPermission[size];
+ }
+ };
+
+ @Nullable
+ public String getBackgroundPermission() {
+ return backgroundPermission;
+ }
+
+ @Nullable
+ public String getGroup() {
+ return group;
+ }
+
+ public int getRequestRes() {
+ return requestRes;
+ }
+
+ public int getProtectionLevel() {
+ return protectionLevel;
+ }
+
+ public boolean isTree() {
+ return tree;
+ }
+
+ @Nullable
+ public ParsedPermissionGroup getParsedPermissionGroup() {
+ return parsedPermissionGroup;
+ }
+
+ public ParsedPermission setProtectionLevel(int value) {
+ protectionLevel = value;
+ return this;
+ }
+
+ public ParsedPermission setParsedPermissionGroup(@Nullable ParsedPermissionGroup value) {
+ parsedPermissionGroup = value;
+ return this;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
new file mode 100644
index 0000000..741c00c
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionGroup.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/** @hide */
+public class ParsedPermissionGroup extends ParsedComponent {
+
+ int requestDetailResourceId;
+ int backgroundRequestResourceId;
+ int backgroundRequestDetailResourceId;
+ int requestRes;
+ int priority;
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public String toString() {
+ return "PermissionGroup{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + getName() + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.requestDetailResourceId);
+ dest.writeInt(this.backgroundRequestResourceId);
+ dest.writeInt(this.backgroundRequestDetailResourceId);
+ dest.writeInt(this.requestRes);
+ dest.writeInt(this.priority);
+ }
+
+ public ParsedPermissionGroup() {
+ }
+
+ protected ParsedPermissionGroup(Parcel in) {
+ super(in);
+ this.requestDetailResourceId = in.readInt();
+ this.backgroundRequestResourceId = in.readInt();
+ this.backgroundRequestDetailResourceId = in.readInt();
+ this.requestRes = in.readInt();
+ this.priority = in.readInt();
+ }
+
+ public static final Parcelable.Creator<ParsedPermissionGroup> CREATOR =
+ new Parcelable.Creator<ParsedPermissionGroup>() {
+ @Override
+ public ParsedPermissionGroup createFromParcel(Parcel source) {
+ return new ParsedPermissionGroup(source);
+ }
+
+ @Override
+ public ParsedPermissionGroup[] newArray(int size) {
+ return new ParsedPermissionGroup[size];
+ }
+ };
+
+ public int getRequestDetailResourceId() {
+ return requestDetailResourceId;
+ }
+
+ public int getBackgroundRequestResourceId() {
+ return backgroundRequestResourceId;
+ }
+
+ public int getBackgroundRequestDetailResourceId() {
+ return backgroundRequestDetailResourceId;
+ }
+
+ public int getRequestRes() {
+ return requestRes;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
new file mode 100644
index 0000000..1884a1e
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.Slog;
+
+import com.android.internal.R;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class ParsedPermissionUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ public static ParseResult<ParsedPermission> parsePermission(ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+ throws IOException, XmlPullParserException {
+ String packageName = pkg.getPackageName();
+ ParsedPermission
+ permission = new ParsedPermission();
+ String tag = "<" + parser.getName() + ">";
+ final ParseResult<ParsedPermission> result;
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
+ try {
+ result = ParsedComponentUtils.parseComponent(
+ permission, tag, pkg, sa, useRoundIcon, input,
+ R.styleable.AndroidManifestPermission_banner,
+ R.styleable.AndroidManifestPermission_description,
+ R.styleable.AndroidManifestPermission_icon,
+ R.styleable.AndroidManifestPermission_label,
+ R.styleable.AndroidManifestPermission_logo,
+ R.styleable.AndroidManifestPermission_name,
+ R.styleable.AndroidManifestPermission_roundIcon);
+ if (result.isError()) {
+ return result;
+ }
+
+ if (sa.hasValue(
+ R.styleable.AndroidManifestPermission_backgroundPermission)) {
+ if ("android".equals(packageName)) {
+ permission.backgroundPermission = sa.getNonResourceString(
+ R.styleable
+ .AndroidManifestPermission_backgroundPermission);
+ } else {
+ Slog.w(TAG, packageName + " defines a background permission. Only the "
+ + "'android' package can do that.");
+ }
+ }
+
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ permission.setGroup(sa.getNonResourceString(
+ R.styleable.AndroidManifestPermission_permissionGroup));
+
+ permission.requestRes = sa.getResourceId(
+ R.styleable.AndroidManifestPermission_request, 0);
+
+ permission.protectionLevel = sa.getInt(
+ R.styleable.AndroidManifestPermission_protectionLevel,
+ PermissionInfo.PROTECTION_NORMAL);
+
+ permission.flags = sa.getInt(
+ R.styleable.AndroidManifestPermission_permissionFlags, 0);
+
+ // For now only platform runtime permissions can be restricted
+ if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) {
+ permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
+ permission.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
+ } else {
+ // The platform does not get to specify conflicting permissions
+ if ((permission.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
+ && (permission.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
+ throw new IllegalStateException("Permission cannot be both soft and hard"
+ + " restricted: " + permission.getName());
+ }
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ // TODO(b/135203078): This is impossible because of default value in above getInt
+ if (permission.protectionLevel == -1) {
+ return input.error("<permission> does not specify protectionLevel");
+ }
+
+ permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
+
+ if (permission.getProtectionFlags() != 0) {
+ if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
+ && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+ == 0
+ && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) !=
+ PermissionInfo.PROTECTION_SIGNATURE) {
+ return input.error("<permission> protectionLevel specifies a non-instant flag "
+ + "but is not based on signature type");
+ }
+ }
+
+ return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
+ }
+
+ @NonNull
+ public static ParseResult<ParsedPermission> parsePermissionTree(ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+ throws IOException, XmlPullParserException {
+ ParsedPermission permission = new ParsedPermission();
+ String tag = "<" + parser.getName() + ">";
+ final ParseResult<ParsedPermission> result;
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
+ try {
+ result = ParsedComponentUtils.parseComponent(
+ permission, tag, pkg, sa, useRoundIcon, input,
+ R.styleable.AndroidManifestPermissionTree_banner,
+ null /*descriptionAttr*/,
+ R.styleable.AndroidManifestPermissionTree_icon,
+ R.styleable.AndroidManifestPermissionTree_label,
+ R.styleable.AndroidManifestPermissionTree_logo,
+ R.styleable.AndroidManifestPermissionTree_name,
+ R.styleable.AndroidManifestPermissionTree_roundIcon);
+ if (result.isError()) {
+ return result;
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ int index = permission.getName().indexOf('.');
+ if (index > 0) {
+ index = permission.getName().indexOf('.', index + 1);
+ }
+ if (index < 0) {
+ return input.error("<permission-tree> name has less than three segments: "
+ + permission.getName());
+ }
+
+ permission.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
+ permission.tree = true;
+
+ return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission,
+ input);
+ }
+
+ @NonNull
+ public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(ParsingPackage pkg,
+ Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)
+ throws IOException, XmlPullParserException {
+ ParsedPermissionGroup
+ permissionGroup = new ParsedPermissionGroup();
+ String tag = "<" + parser.getName() + ">";
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
+ try {
+ ParseResult<ParsedPermissionGroup> result = ParsedComponentUtils.parseComponent(
+ permissionGroup, tag, pkg, sa, useRoundIcon, input,
+ R.styleable.AndroidManifestPermissionGroup_banner,
+ R.styleable.AndroidManifestPermissionGroup_description,
+ R.styleable.AndroidManifestPermissionGroup_icon,
+ R.styleable.AndroidManifestPermissionGroup_label,
+ R.styleable.AndroidManifestPermissionGroup_logo,
+ R.styleable.AndroidManifestPermissionGroup_name,
+ R.styleable.AndroidManifestPermissionGroup_roundIcon);
+ if (result.isError()) {
+ return result;
+ }
+
+ // @formatter:off
+ permissionGroup.requestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
+ permissionGroup.backgroundRequestResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0);
+ permissionGroup.backgroundRequestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
+ permissionGroup.requestRes = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0);
+ permissionGroup.flags = sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0);
+ permissionGroup.priority = sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0);
+ // @formatter:on
+ } finally {
+ sa.recycle();
+ }
+
+ return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permissionGroup,
+ input);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcess.java b/core/java/android/content/pm/parsing/component/ParsedProcess.java
new file mode 100644
index 0000000..da7bf98
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedProcess.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static java.util.Collections.emptySet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+
+import java.util.Set;
+
+/** @hide */
+@DataClass(genGetters = true, genSetters = false, genParcelable = true, genAidl = false,
+ genBuilder = false)
+public class ParsedProcess implements Parcelable {
+
+ @NonNull
+ protected String name;
+ @NonNull
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
+ protected Set<String> deniedPermissions = emptySet();
+
+ public ParsedProcess() {
+ }
+
+ public ParsedProcess(@NonNull ParsedProcess other) {
+ name = other.name;
+ deniedPermissions = new ArraySet<>(other.deniedPermissions);
+ }
+
+ public void addStateFrom(@NonNull ParsedProcess other) {
+ deniedPermissions = CollectionUtils.addAll(deniedPermissions, other.deniedPermissions);
+ }
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public ParsedProcess(
+ @NonNull String name,
+ @NonNull Set<String> deniedPermissions) {
+ this.name = name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.deniedPermissions = deniedPermissions;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, deniedPermissions);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getName() {
+ return name;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull Set<String> getDeniedPermissions() {
+ return deniedPermissions;
+ }
+
+ @DataClass.Generated.Member
+ static Parcelling<Set<String>> sParcellingForDeniedPermissions =
+ Parcelling.Cache.get(
+ Parcelling.BuiltIn.ForInternedStringSet.class);
+ static {
+ if (sParcellingForDeniedPermissions == null) {
+ sParcellingForDeniedPermissions = Parcelling.Cache.put(
+ new Parcelling.BuiltIn.ForInternedStringSet());
+ }
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeString(name);
+ sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected ParsedProcess(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ String _name = in.readString();
+ Set<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
+
+ this.name = _name;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, name);
+ this.deniedPermissions = _deniedPermissions;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, deniedPermissions);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ParsedProcess> CREATOR
+ = new Parcelable.Creator<ParsedProcess>() {
+ @Override
+ public ParsedProcess[] newArray(int size) {
+ return new ParsedProcess[size];
+ }
+
+ @Override
+ public ParsedProcess createFromParcel(@NonNull Parcel in) {
+ return new ParsedProcess(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1581452315946L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedProcess.java",
+ inputSignatures = "protected @android.annotation.NonNull java.lang.String name\nprotected @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\npublic void addStateFrom(android.content.pm.parsing.component.ParsedProcess)\nclass ParsedProcess extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
new file mode 100644
index 0000000..4825066
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedProcessUtils.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import android.annotation.NonNull;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/** @hide */
+public class ParsedProcessUtils {
+
+ private static final String TAG = ParsingUtils.TAG;
+
+ @NonNull
+ private static ParseResult<Set<String>> parseDenyPermission(Set<String> perms,
+ Resources res, XmlResourceParser parser, ParseInput input)
+ throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
+ try {
+ String perm = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestDenyPermission_name, 0);
+ if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+ perms = CollectionUtils.add(perms, perm);
+ }
+ } finally {
+ sa.recycle();
+ }
+ XmlUtils.skipCurrentTag(parser);
+ return input.success(perms);
+ }
+
+ @NonNull
+ private static ParseResult<Set<String>> parseAllowPermission(Set<String> perms, Resources res,
+ XmlResourceParser parser, ParseInput input)
+ throws IOException, XmlPullParserException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
+ try {
+ String perm = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestAllowPermission_name, 0);
+ if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
+ perms = CollectionUtils.remove(perms, perm);
+ }
+ } finally {
+ sa.recycle();
+ }
+ XmlUtils.skipCurrentTag(parser);
+ return input.success(perms);
+ }
+
+ @NonNull
+ private static ParseResult<ParsedProcess> parseProcess(Set<String> perms, String[] separateProcesses,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+ ParseInput input) throws IOException, XmlPullParserException {
+ ParsedProcess proc = new ParsedProcess();
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
+ try {
+ if (perms != null) {
+ proc.deniedPermissions = new ArraySet<>(perms);
+ }
+
+ proc.name = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestProcess_process, 0);
+ ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+ pkg.getPackageName(), pkg.getPackageName(), proc.name, flags, separateProcesses,
+ input);
+ if (processNameResult.isError()) {
+ return input.error(processNameResult);
+ }
+
+ proc.name = processNameResult.getResult();
+
+ if (proc.name == null || proc.name.length() <= 0) {
+ return input.error("<process> does not specify android:process");
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ int type;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ ParseResult<?> result;
+
+ String tagName = parser.getName();
+ switch (tagName) {
+ case "deny-permission":
+ ParseResult<Set<String>> denyResult = parseDenyPermission(
+ proc.deniedPermissions, res, parser, input);
+ result = denyResult;
+ if (denyResult.isSuccess()) {
+ proc.deniedPermissions = denyResult.getResult();
+ }
+ break;
+ case "allow-permission":
+ ParseResult<Set<String>> allowResult = parseAllowPermission(
+ proc.deniedPermissions, res, parser, input);
+ result = allowResult;
+ if (allowResult.isSuccess()) {
+ proc.deniedPermissions = allowResult.getResult();
+ }
+ break;
+ default:
+ result = ParsingUtils.unknownTag("<process>", pkg, parser, input);
+ break;
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ return input.success(proc);
+ }
+
+ @NonNull
+ public static ParseResult<ArrayMap<String, ParsedProcess>> parseProcesses(
+ String[] separateProcesses, ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, int flags, ParseInput input)
+ throws IOException, XmlPullParserException {
+ Set<String> deniedPerms = null;
+ ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
+
+ int type;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ ParseResult<?> result;
+
+ String tagName = parser.getName();
+ switch (tagName) {
+ case "deny-permission":
+ ParseResult<Set<String>> denyResult = parseDenyPermission(deniedPerms, res,
+ parser, input);
+ result = denyResult;
+ if (denyResult.isSuccess()) {
+ deniedPerms = denyResult.getResult();
+ }
+ break;
+ case "allow-permission":
+ ParseResult<Set<String>> allowResult = parseAllowPermission(deniedPerms, res,
+ parser, input);
+ result = allowResult;
+ if (allowResult.isSuccess()) {
+ deniedPerms = allowResult.getResult();
+ }
+ break;
+ case "process":
+ ParseResult<ParsedProcess> processResult = parseProcess(deniedPerms,
+ separateProcesses, pkg, res, parser, flags, input);
+ result = processResult;
+ if (processResult.isSuccess()) {
+ ParsedProcess process = processResult.getResult();
+ if (processes.put(process.name, process) != null) {
+ result = input.error(
+ "<process> specified existing name '" + process.name + "'");
+ }
+ }
+ break;
+ default:
+ result = ParsingUtils.unknownTag("<processes>", pkg, parser, input);
+ break;
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+
+ }
+
+ return input.success(processes);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProvider.java b/core/java/android/content/pm/parsing/component/ParsedProvider.java
new file mode 100644
index 0000000..d2c531d
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedProvider.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.PathPermission;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PatternMatcher;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+public class ParsedProvider extends ParsedMainComponent {
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String authority;
+ boolean syncable;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String readPermission;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String writePermission;
+ boolean grantUriPermissions;
+ boolean forceUriPermissions;
+ boolean multiProcess;
+ int initOrder;
+ @Nullable
+ PatternMatcher[] uriPermissionPatterns;
+ @Nullable
+ PathPermission[] pathPermissions;
+
+ public ParsedProvider(ParsedProvider other) {
+ super(other);
+
+ this.authority = other.authority;
+ this.syncable = other.syncable;
+ this.readPermission = other.readPermission;
+ this.writePermission = other.writePermission;
+ this.grantUriPermissions = other.grantUriPermissions;
+ this.forceUriPermissions = other.forceUriPermissions;
+ this.multiProcess = other.multiProcess;
+ this.initOrder = other.initOrder;
+ this.uriPermissionPatterns = other.uriPermissionPatterns;
+ this.pathPermissions = other.pathPermissions;
+ }
+
+ public void setAuthority(String authority) {
+ this.authority = TextUtils.safeIntern(authority);
+ }
+
+ public void setSyncable(boolean syncable) {
+ this.syncable = syncable;
+ }
+
+ public void setReadPermission(String readPermission) {
+ // Empty string must be converted to null
+ this.readPermission = TextUtils.isEmpty(readPermission)
+ ? null : readPermission.intern();
+ }
+
+ public void setWritePermission(String writePermission) {
+ // Empty string must be converted to null
+ this.writePermission = TextUtils.isEmpty(writePermission)
+ ? null : writePermission.intern();
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Provider{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ ComponentName.appendShortString(sb, getPackageName(), getName());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ sForString.parcel(this.authority, dest, flags);
+ dest.writeBoolean(this.syncable);
+ sForString.parcel(this.readPermission, dest, flags);
+ sForString.parcel(this.writePermission, dest, flags);
+ dest.writeBoolean(this.grantUriPermissions);
+ dest.writeBoolean(this.forceUriPermissions);
+ dest.writeBoolean(this.multiProcess);
+ dest.writeInt(this.initOrder);
+ dest.writeTypedArray(this.uriPermissionPatterns, flags);
+ dest.writeTypedArray(this.pathPermissions, flags);
+ }
+
+ public ParsedProvider() {
+ }
+
+ protected ParsedProvider(Parcel in) {
+ super(in);
+ //noinspection ConstantConditions
+ this.authority = sForString.unparcel(in);
+ this.syncable = in.readBoolean();
+ this.readPermission = sForString.unparcel(in);
+ this.writePermission = sForString.unparcel(in);
+ this.grantUriPermissions = in.readBoolean();
+ this.forceUriPermissions = in.readBoolean();
+ this.multiProcess = in.readBoolean();
+ this.initOrder = in.readInt();
+ this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
+ this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
+ }
+
+ public static final Parcelable.Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
+ @Override
+ public ParsedProvider createFromParcel(Parcel source) {
+ return new ParsedProvider(source);
+ }
+
+ @Override
+ public ParsedProvider[] newArray(int size) {
+ return new ParsedProvider[size];
+ }
+ };
+
+ @NonNull
+ public String getAuthority() {
+ return authority;
+ }
+
+ public boolean isSyncable() {
+ return syncable;
+ }
+
+ @Nullable
+ public String getReadPermission() {
+ return readPermission;
+ }
+
+ @Nullable
+ public String getWritePermission() {
+ return writePermission;
+ }
+
+ public boolean isGrantUriPermissions() {
+ return grantUriPermissions;
+ }
+
+ public boolean isForceUriPermissions() {
+ return forceUriPermissions;
+ }
+
+ public boolean isMultiProcess() {
+ return multiProcess;
+ }
+
+ public int getInitOrder() {
+ return initOrder;
+ }
+
+ @Nullable
+ public PatternMatcher[] getUriPermissionPatterns() {
+ return uriPermissionPatterns;
+ }
+
+ @Nullable
+ public PathPermission[] getPathPermissions() {
+ return pathPermissions;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
new file mode 100644
index 0000000..aa5ea8d
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageParser;
+import android.content.pm.PathPermission;
+import android.content.pm.ProviderInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.os.PatternMatcher;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedProviderUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+ boolean useRoundIcon, ParseInput input)
+ throws IOException, XmlPullParserException {
+ String authority;
+ boolean visibleToEphemeral;
+
+ final int targetSdkVersion = pkg.getTargetSdkVersion();
+ final String packageName = pkg.getPackageName();
+ final ParsedProvider provider = new ParsedProvider();
+ final String tag = parser.getName();
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider);
+ try {
+ ParseResult<ParsedProvider> result =
+ ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses,
+ pkg, sa, flags, useRoundIcon, input,
+ R.styleable.AndroidManifestProvider_banner,
+ R.styleable.AndroidManifestProvider_description,
+ R.styleable.AndroidManifestProvider_directBootAware,
+ R.styleable.AndroidManifestProvider_enabled,
+ R.styleable.AndroidManifestProvider_icon,
+ R.styleable.AndroidManifestProvider_label,
+ R.styleable.AndroidManifestProvider_logo,
+ R.styleable.AndroidManifestProvider_name,
+ R.styleable.AndroidManifestProvider_process,
+ R.styleable.AndroidManifestProvider_roundIcon,
+ R.styleable.AndroidManifestProvider_splitName);
+ if (result.isError()) {
+ return result;
+ }
+
+ authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0);
+
+ // For compatibility, applications targeting API level 16 or lower
+ // should have their content providers exported by default, unless they
+ // specify otherwise.
+ provider.exported = sa.getBoolean(R.styleable.AndroidManifestProvider_exported,
+ targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1);
+
+ provider.syncable = sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false);
+
+ String permission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestProvider_permission, 0);
+ String readPermission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestProvider_readPermission, 0);
+ if (readPermission == null) {
+ readPermission = permission;
+ }
+ if (readPermission == null) {
+ provider.setReadPermission(pkg.getPermission());
+ } else {
+ provider.setReadPermission(readPermission);
+ }
+ String writePermission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestProvider_writePermission, 0);
+ if (writePermission == null) {
+ writePermission = permission;
+ }
+ if (writePermission == null) {
+ provider.setWritePermission(pkg.getPermission());
+ } else {
+ provider.setWritePermission(writePermission);
+ }
+
+ provider.grantUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false);
+ provider.forceUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions, false);
+ provider.multiProcess = sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false);
+ provider.initOrder = sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0);
+
+ provider.flags |= flag(ProviderInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestProvider_singleUser, sa);
+
+ visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
+ if (visibleToEphemeral) {
+ provider.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ pkg.setVisibleToInstantApps(true);
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ if (pkg.isCantSaveState()) {
+ // A heavy-weight application can not have providers in its main process
+ if (Objects.equals(provider.getProcessName(), packageName)) {
+ return input.error("Heavy-weight applications can not have providers"
+ + " in main process");
+ }
+ }
+
+ if (authority == null) {
+ return input.error("<provider> does not include authorities attribute");
+ }
+ if (authority.length() <= 0) {
+ return input.error("<provider> has empty authorities attribute");
+ }
+ provider.setAuthority(authority);
+
+ return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input);
+ }
+
+ @NonNull
+ private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag,
+ Resources res, XmlResourceParser parser, boolean visibleToEphemeral,
+ ParsedProvider provider, ParseInput input)
+ throws XmlPullParserException, IOException {
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ final ParseResult result;
+ switch (name) {
+ case "intent-filter":
+ ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+ .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral,
+ true /*allowGlobs*/, false /*allowAutoVerify*/,
+ false /*allowImplicitEphemeralVisibility*/,
+ false /*failOnNoActions*/, input);
+ result = intentResult;
+ if (intentResult.isSuccess()) {
+ ParsedIntentInfo intent = intentResult.getResult();
+ provider.order = Math.max(intent.getOrder(), provider.order);
+ provider.addIntent(intent);
+ }
+ break;
+ case "meta-data":
+ result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
+ break;
+ case "grant-uri-permission": {
+ result = parseGrantUriPermission(provider, pkg, res, parser, input);
+ break;
+ }
+ case "path-permission": {
+ result = parsePathPermission(provider, pkg, res, parser, input);
+ break;
+ }
+ default:
+ result = ParsingUtils.unknownTag(tag, pkg, parser, input);
+ break;
+ }
+
+ if (result.isError()) {
+ return input.error(result);
+ }
+ }
+
+ return input.success(provider);
+ }
+
+ @NonNull
+ private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProvider provider,
+ ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
+ TypedArray sa = resources.obtainAttributes(parser,
+ R.styleable.AndroidManifestGrantUriPermission);
+ try {
+ String name = parser.getName();
+ // Pattern has priority over prefix over literal path
+ PatternMatcher pa = null;
+ String str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
+ if (str != null) {
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ } else {
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
+ if (str != null) {
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
+ } else {
+ str = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestGrantUriPermission_path, 0);
+ if (str != null) {
+ pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
+ }
+ }
+ }
+
+ if (pa != null) {
+ if (provider.uriPermissionPatterns == null) {
+ provider.uriPermissionPatterns = new PatternMatcher[1];
+ provider.uriPermissionPatterns[0] = pa;
+ } else {
+ final int N = provider.uriPermissionPatterns.length;
+ PatternMatcher[] newp = new PatternMatcher[N + 1];
+ System.arraycopy(provider.uriPermissionPatterns, 0, newp, 0, N);
+ newp[N] = pa;
+ provider.uriPermissionPatterns = newp;
+ }
+ provider.grantUriPermissions = true;
+ } else {
+ if (PackageParser.RIGID_PARSER) {
+ return input.error("No path, pathPrefix, or pathPattern for <path-permission>");
+ }
+
+ Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at "
+ + pkg.getBaseCodePath() + " " + parser.getPositionDescription());
+ }
+
+ return input.success(provider);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
+ private static ParseResult<ParsedProvider> parsePathPermission(ParsedProvider provider,
+ ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) {
+ TypedArray sa = resources.obtainAttributes(parser,
+ R.styleable.AndroidManifestPathPermission);
+ try {
+ String name = parser.getName();
+
+ String permission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestPathPermission_permission, 0);
+ String readPermission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestPathPermission_readPermission, 0);
+ if (readPermission == null) {
+ readPermission = permission;
+ }
+ String writePermission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestPathPermission_writePermission, 0);
+ if (writePermission == null) {
+ writePermission = permission;
+ }
+
+ boolean havePerm = false;
+ if (readPermission != null) {
+ readPermission = readPermission.intern();
+ havePerm = true;
+ }
+ if (writePermission != null) {
+ writePermission = writePermission.intern();
+ havePerm = true;
+ }
+
+ if (!havePerm) {
+ if (PackageParser.RIGID_PARSER) {
+ return input.error(
+ "No readPermission or writePermission for <path-permission>");
+ }
+ Slog.w(TAG, "No readPermission or writePermission for <path-permission>: "
+ + name + " at " + pkg.getBaseCodePath() + " " + parser.getPositionDescription());
+ return input.success(provider);
+ }
+
+ // Advanced has priority over simply over prefix over literal
+ PathPermission pa = null;
+ String path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
+ if (path != null) {
+ pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission,
+ writePermission);
+ } else {
+ path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathPattern, 0);
+ if (path != null) {
+ pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB,
+ readPermission, writePermission);
+ } else {
+ path = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
+ if (path != null) {
+ pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission,
+ writePermission);
+ } else {
+ path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_path, 0);
+ if (path != null) {
+ pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL,
+ readPermission, writePermission);
+ }
+ }
+ }
+ }
+
+ if (pa != null) {
+ if (provider.pathPermissions == null) {
+ provider.pathPermissions = new PathPermission[1];
+ provider.pathPermissions[0] = pa;
+ } else {
+ final int N = provider.pathPermissions.length;
+ PathPermission[] newp = new PathPermission[N + 1];
+ System.arraycopy(provider.pathPermissions, 0, newp, 0, N);
+ newp[N] = pa;
+ provider.pathPermissions = newp;
+ }
+ } else {
+ if (PackageParser.RIGID_PARSER) {
+ return input.error(
+ "No path, pathPrefix, or pathPattern for <path-permission>");
+ }
+
+ Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
+ + name + " at " + pkg.getBaseCodePath()
+ + " "
+ + parser.getPositionDescription());
+ }
+
+ return input.success(provider);
+ } finally {
+ sa.recycle();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedService.java b/core/java/android/content/pm/parsing/component/ParsedService.java
new file mode 100644
index 0000000..591eef7
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedService.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.ParsingPackageImpl.sForString;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+
+/** @hide **/
+public class ParsedService extends ParsedMainComponent {
+
+ int foregroundServiceType;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ private String permission;
+
+ public ParsedService(ParsedService other) {
+ super(other);
+ this.foregroundServiceType = other.foregroundServiceType;
+ this.permission = other.permission;
+ }
+
+ public ParsedMainComponent setPermission(String permission) {
+ // Empty string must be converted to null
+ this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
+ return this;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Service{");
+ sb.append(Integer.toHexString(System.identityHashCode(this)));
+ sb.append(' ');
+ ComponentName.appendShortString(sb, getPackageName(), getName());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(this.foregroundServiceType);
+ sForString.parcel(this.permission, dest, flags);
+ }
+
+ public ParsedService() {
+ }
+
+ protected ParsedService(Parcel in) {
+ super(in);
+ this.foregroundServiceType = in.readInt();
+ this.permission = sForString.unparcel(in);
+ }
+
+ public static final Parcelable.Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
+ @Override
+ public ParsedService createFromParcel(Parcel source) {
+ return new ParsedService(source);
+ }
+
+ @Override
+ public ParsedService[] newArray(int size) {
+ return new ParsedService[size];
+ }
+ };
+
+ public int getForegroundServiceType() {
+ return foregroundServiceType;
+ }
+
+ @Nullable
+ public String getPermission() {
+ return permission;
+ }
+}
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
new file mode 100644
index 0000000..8a8a066
--- /dev/null
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 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.parsing.component;
+
+import static android.content.pm.parsing.component.ComponentParseUtils.flag;
+
+import android.annotation.NonNull;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/** @hide */
+public class ParsedServiceUtils {
+
+ private static final String TAG = ParsingPackageUtils.TAG;
+
+ @NonNull
+ public static ParseResult<ParsedService> parseService(String[] separateProcesses,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
+ boolean useRoundIcon, ParseInput input)
+ throws XmlPullParserException, IOException {
+ boolean visibleToEphemeral;
+ boolean setExported;
+
+ final String packageName = pkg.getPackageName();
+ final ParsedService service = new ParsedService();
+ String tag = parser.getName();
+
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestService);
+ try {
+ ParseResult<ParsedService> result = ParsedMainComponentUtils.parseMainComponent(
+ service, tag, separateProcesses, pkg, sa, flags, useRoundIcon, input,
+ R.styleable.AndroidManifestService_banner,
+ R.styleable.AndroidManifestService_description,
+ R.styleable.AndroidManifestService_directBootAware,
+ R.styleable.AndroidManifestService_enabled,
+ R.styleable.AndroidManifestService_icon,
+ R.styleable.AndroidManifestService_label,
+ R.styleable.AndroidManifestService_logo,
+ R.styleable.AndroidManifestService_name,
+ R.styleable.AndroidManifestService_process,
+ R.styleable.AndroidManifestService_roundIcon,
+ R.styleable.AndroidManifestService_splitName
+ );
+
+ if (result.isError()) {
+ return result;
+ }
+
+ setExported = sa.hasValue(R.styleable.AndroidManifestService_exported);
+ if (setExported) {
+ service.exported = sa.getBoolean(R.styleable.AndroidManifestService_exported,
+ false);
+ }
+
+ String permission = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestService_permission, 0);
+ service.setPermission(permission != null ? permission : pkg.getPermission());
+
+ service.foregroundServiceType = sa.getInt(
+ R.styleable.AndroidManifestService_foregroundServiceType,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
+
+ service.flags |= flag(ServiceInfo.FLAG_STOP_WITH_TASK,
+ R.styleable.AndroidManifestService_stopWithTask, sa)
+ | flag(ServiceInfo.FLAG_ISOLATED_PROCESS,
+ R.styleable.AndroidManifestService_isolatedProcess, sa)
+ | flag(ServiceInfo.FLAG_EXTERNAL_SERVICE,
+ R.styleable.AndroidManifestService_externalService, sa)
+ | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
+ R.styleable.AndroidManifestService_useAppZygote, sa)
+ | flag(ServiceInfo.FLAG_SINGLE_USER,
+ R.styleable.AndroidManifestService_singleUser, sa);
+
+ visibleToEphemeral = sa.getBoolean(
+ R.styleable.AndroidManifestService_visibleToInstantApps, false);
+ if (visibleToEphemeral) {
+ service.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
+ pkg.setVisibleToInstantApps(true);
+ }
+ } finally {
+ sa.recycle();
+ }
+
+ if (pkg.isCantSaveState()) {
+ // A heavy-weight application can not have services in its main process
+ // We can do direct compare because we intern all strings.
+ if (Objects.equals(service.getProcessName(), packageName)) {
+ return input.error("Heavy-weight applications can not have services "
+ + "in main process");
+ }
+ }
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG
+ || parser.getDepth() > depth)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ final ParseResult parseResult;
+ switch (parser.getName()) {
+ case "intent-filter":
+ ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils
+ .parseIntentFilter(service, pkg, res, parser, visibleToEphemeral,
+ true /*allowGlobs*/, false /*allowAutoVerify*/,
+ false /*allowImplicitEphemeralVisibility*/,
+ false /*failOnNoActions*/, input);
+ parseResult = intentResult;
+ if (intentResult.isSuccess()) {
+ ParsedIntentInfo intent = intentResult.getResult();
+ service.order = Math.max(intent.getOrder(), service.order);
+ service.addIntent(intent);
+ }
+ break;
+ case "meta-data":
+ parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
+ break;
+ default:
+ parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
+ break;
+ }
+
+ if (parseResult.isError()) {
+ return input.error(parseResult);
+ }
+ }
+
+ if (!setExported) {
+ service.exported = service.getIntents().size() > 0;
+ }
+
+ return input.success(service);
+ }
+}
diff --git a/core/java/android/content/pm/parsing/result/ParseInput.java b/core/java/android/content/pm/parsing/result/ParseInput.java
new file mode 100644
index 0000000..c468506
--- /dev/null
+++ b/core/java/android/content/pm/parsing/result/ParseInput.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.parsing.result;
+
+import android.annotation.Hide;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+
+/**
+ * Used as a method parameter which is then transformed into a {@link ParseResult}. This is
+ * generalized as it doesn't matter what type this input is for. It's simply to hide the
+ * methods of {@link ParseResult}.
+ *
+ * @hide
+ */
+public interface ParseInput {
+
+ <ResultType> ParseResult<ResultType> success(ResultType result);
+
+ /** @see #error(int, String, Exception) */
+ <ResultType> ParseResult<ResultType> error(int parseError);
+
+ /**
+ * This will assign errorCode to {@link PackageManager#INSTALL_PARSE_FAILED_MANIFEST_MALFORMED}.
+ * @see #error(int, String, Exception)
+ */
+ <ResultType> ParseResult<ResultType> error(@NonNull String parseError);
+
+ /** @see #error(int, String, Exception) */
+ <ResultType> ParseResult<ResultType> error(int parseError, @Nullable String errorMessage);
+
+ /**
+ * Marks this as an error result. When this method is called, the return value <b>must</b>
+ * be returned to the exit of the parent method that took in this {@link ParseInput} as a
+ * parameter.
+ *
+ * The calling site of that method is then expected to check the result for error, and
+ * continue to bubble up if it is an error.
+ *
+ * Or, if the code explicitly handles an error,
+ * {@link ParseResult#ignoreError()} should be called.
+ *
+ * If the result {@link ParseResult#isSuccess()}, then it can be used as-is, as
+ * overlapping/consecutive successes are allowed.
+ */
+ <ResultType> ParseResult<ResultType> error(int parseError, @Nullable String errorMessage,
+ @Nullable Exception exception);
+
+ /**
+ * Moves the error in {@param result} to this input's type. In practice this does nothing
+ * but cast the type of the {@link ParseResult} for type safety, since the parameter
+ * and the receiver should be the same object.
+ */
+ <ResultType> ParseResult<ResultType> error(ParseResult result);
+}
diff --git a/core/java/android/content/pm/parsing/result/ParseResult.java b/core/java/android/content/pm/parsing/result/ParseResult.java
new file mode 100644
index 0000000..338048c
--- /dev/null
+++ b/core/java/android/content/pm/parsing/result/ParseResult.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.parsing.result;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+
+/**
+ * The output side of {@link ParseInput}, which must result from a method call on
+ * {@link ParseInput}.
+ *
+ * When using this class, keep in mind that all {@link ParseInput}s and {@link ParseResult}s
+ * are the exact same object, scoped to a per {@link PackageParser} instance, per thread basis,
+ * thrown around and casted everywhere for type safety.
+ *
+ * @hide
+ */
+public interface ParseResult<ResultType> {
+
+ /**
+ * Un-marks this result as an error, also allowing it to be re-used as {@link ParseInput}.
+ *
+ * This should only be used in cases where it's absolutely certain that error handling is
+ * irrelevant. Such as for backwards compatibility where it previously didn't fail and that
+ * behavior has to be maintained.
+ *
+ * Mostly an alias for readability.
+ */
+ void ignoreError();
+
+ /**
+ * Returns true if the result is not an error and thus contains a valid object.
+ *
+ * For backwards-compat reasons, it's possible to have a successful result with a null
+ * result object, depending on the behavior of the parsing method.
+ *
+ * It is expected that every method calls this to check for an error state to bubble up
+ * the error to its parent method after every parse method call.
+ *
+ * It is not always necessary to check this, as it is valid to return any ParseResult from
+ * a method so long as the type matches <b>without casting it</b>.
+ *
+ * The infrastructure is set up such that as long as a result is the proper type and
+ * the right side of success vs. error, it can be bubble up through all its parent methods.
+ */
+ boolean isSuccess();
+
+ /**
+ * Opposite of {@link #isSuccess()} for readability.
+ */
+ boolean isError();
+
+ ResultType getResult();
+
+ int getErrorCode();
+
+ @Nullable
+ String getErrorMessage();
+
+ @Nullable
+ Exception getException();
+}
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
new file mode 100644
index 0000000..9b22f09
--- /dev/null
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 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.parsing.result;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.parsing.ParsingUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/** @hide */
+public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
+
+ private static final String TAG = ParsingUtils.TAG;
+
+ private static final boolean DEBUG_FILL_STACK_TRACE = false;
+
+ private static final boolean DEBUG_LOG_ON_ERROR = false;
+
+ private Object result;
+
+ private int errorCode = PackageManager.INSTALL_SUCCEEDED;
+
+ @Nullable
+ private String errorMessage;
+
+ @Nullable
+ private Exception exception;
+
+ public ParseInput reset() {
+ this.result = null;
+ this.errorCode = PackageManager.INSTALL_SUCCEEDED;
+ this.errorMessage = null;
+ this.exception = null;
+ return this;
+ }
+
+ @Override
+ public void ignoreError() {
+ reset();
+ }
+
+ @Override
+ public <ResultType> ParseResult<ResultType> success(ResultType result) {
+ if (errorCode != PackageManager.INSTALL_SUCCEEDED || errorMessage != null) {
+ throw new IllegalStateException("Cannot set to success after set to error, was "
+ + errorMessage, exception);
+ }
+ this.result = result;
+ //noinspection unchecked
+ return (ParseResult<ResultType>) this;
+ }
+
+ @Override
+ public <ResultType> ParseResult<ResultType> error(int parseError) {
+ return error(parseError, null);
+ }
+
+ @Override
+ public <ResultType> ParseResult<ResultType> error(@NonNull String parseError) {
+ return error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, parseError);
+ }
+
+ @Override
+ public <ResultType> ParseResult<ResultType> error(int errorCode,
+ @Nullable String errorMessage) {
+ return error(errorCode, errorMessage, null);
+ }
+
+ @Override
+ public <ResultType> ParseResult<ResultType> error(ParseResult intentResult) {
+ return error(intentResult.getErrorCode(), intentResult.getErrorMessage());
+ }
+
+ @Override
+ public <ResultType> ParseResult<ResultType> error(int errorCode, @Nullable String errorMessage,
+ Exception exception) {
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ this.exception = exception;
+
+ if (DEBUG_FILL_STACK_TRACE) {
+ if (exception == null) {
+ this.exception = new Exception();
+ }
+ }
+
+ if (DEBUG_LOG_ON_ERROR) {
+ Exception exceptionToLog = this.exception != null ? this.exception : new Exception();
+ Log.w(TAG, "ParseInput set to error " + errorCode + ", " + errorMessage,
+ exceptionToLog);
+ }
+
+ //noinspection unchecked
+ return (ParseResult<ResultType>) this;
+ }
+
+ @Override
+ public Object getResult() {
+ return this.result;
+ }
+
+ @Override
+ public boolean isSuccess() {
+ return errorCode == PackageManager.INSTALL_SUCCEEDED;
+ }
+
+ @Override
+ public boolean isError() {
+ return !isSuccess();
+ }
+
+ @Override
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+ @Nullable
+ @Override
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ @Nullable
+ @Override
+ public Exception getException() {
+ return exception;
+ }
+}
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index 0a76bed..7714dd8 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -31,6 +31,114 @@
public class AdbManager {
private static final String TAG = "AdbManager";
+ /**
+ * Action indicating the state change of wireless debugging. Can be either
+ * STATUS_CONNECTED
+ * STATUS_DISCONNECTED
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION =
+ "com.android.server.adb.WIRELESS_DEBUG_STATUS";
+
+ /**
+ * Contains the list of paired devices.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION =
+ "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES";
+
+ /**
+ * Action indicating the status of a pairing. Can be either
+ * WIRELESS_STATUS_FAIL
+ * WIRELESS_STATUS_SUCCESS
+ * WIRELESS_STATUS_CANCELLED
+ * WIRELESS_STATUS_PAIRING_CODE
+ * WIRELESS_STATUS_CONNECTED
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION =
+ "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT";
+
+ /**
+ * Extra containing the PairDevice map of paired/pairing devices.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEVICES_EXTRA = "devices_map";
+
+ /**
+ * The status of the pairing/unpairing.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_STATUS_EXTRA = "status";
+
+ /**
+ * The PairDevice.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_PAIR_DEVICE_EXTRA = "pair_device";
+
+ /**
+ * The six-digit pairing code.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_PAIRING_CODE_EXTRA = "pairing_code";
+
+ /**
+ * The adb connection/pairing port that was opened.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_PORT_EXTRA = "adb_port";
+
+ /**
+ * Status indicating the pairing/unpairing failed.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_FAIL = 0;
+
+ /**
+ * Status indicating the pairing/unpairing succeeded.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_SUCCESS = 1;
+
+ /**
+ * Status indicating the pairing/unpairing was cancelled.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_CANCELLED = 2;
+
+ /**
+ * Status indicating the pairing code for pairing.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_PAIRING_CODE = 3;
+
+ /**
+ * Status indicating wireless debugging is connected.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_CONNECTED = 4;
+
+ /**
+ * Status indicating wireless debugging is disconnected.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_DISCONNECTED = 5;
+
private final Context mContext;
private final IAdbManager mService;
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index 51eb7fc..0bd9f19 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -42,7 +42,7 @@
/**
* Returns {@code true} if ADB debugging is enabled.
*/
- public abstract boolean isAdbEnabled();
+ public abstract boolean isAdbEnabled(byte transportType);
/**
* Returns the file that contains all of the ADB keys used by the device.
diff --git a/core/java/android/debug/AdbTransportType.aidl b/core/java/android/debug/AdbTransportType.aidl
new file mode 100644
index 0000000..6904615
--- /dev/null
+++ b/core/java/android/debug/AdbTransportType.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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.debug;
+
+/** @hide */
+@Backing(type="byte")
+enum AdbTransportType {
+ USB,
+ WIFI,
+}
+
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
index c48fc07..aea7633 100644
--- a/core/java/android/debug/IAdbManager.aidl
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -43,6 +43,62 @@
void clearDebuggingKeys();
/**
+ * Allow ADB wireless debugging on the connected network. If {@code alwaysAllow}
+ * is {@code true}, add {@code bssid} to list of networks that the user has
+ * approved.
+ *
+ * @param alwaysAllow if true, add permanently to list of allowed networks
+ * @param bssid BSSID of the network
+ */
+ void allowWirelessDebugging(boolean alwaysAllow, String bssid);
+
+ /**
+ * Deny ADB wireless debugging on the connected network.
+ */
+ void denyWirelessDebugging();
+
+ /**
+ * Returns a Map<String, PairDevice> with the key fingerprint mapped to the device information.
+ */
+ Map getPairedDevices();
+
+ /**
+ * Unpair the device identified by the key fingerprint it uses.
+ *
+ * @param fingerprint fingerprint of the key the device is using.
+ */
+ void unpairDevice(String fingerprint);
+
+ /**
+ * Enables pairing by pairing code. The result of the enable will be sent via intent action
+ * {@link android.debug.AdbManager#WIRELESS_DEBUG_ENABLE_DISCOVER_ACTION}. Furthermore, the
+ * pairing code will also be sent in the intent as an extra
+ * @{link android.debug.AdbManager#WIRELESS_PAIRING_CODE_EXTRA}. Note that only one
+ * pairing method can be enabled at a time, either by pairing code, or by QR code.
+ */
+ void enablePairingByPairingCode();
+
+ /**
+ * Enables pairing by QR code. The result of the enable will be sent via intent action
+ * {@link android.debug.AdbManager#WIRELESS_DEBUG_ENABLE_DISCOVER_ACTION}. Note that only one
+ * pairing method can be enabled at a time, either by pairing code, or by QR code.
+ *
+ * @param serviceName The MDNS service name parsed from the QR code.
+ * @param password The password parsed from the QR code.
+ */
+ void enablePairingByQrCode(String serviceName, String password);
+
+ /**
+ * Returns the network port that adb wireless server is running on.
+ */
+ int getAdbWirelessPort();
+
+ /**
+ * Disables pairing.
+ */
+ void disablePairing();
+
+ /**
* Returns true if device supports secure Adb over Wi-Fi.
*/
boolean isAdbWifiSupported();
diff --git a/core/java/android/debug/IAdbTransport.aidl b/core/java/android/debug/IAdbTransport.aidl
index 77211fc93..f018813 100644
--- a/core/java/android/debug/IAdbTransport.aidl
+++ b/core/java/android/debug/IAdbTransport.aidl
@@ -16,7 +16,9 @@
package android.debug;
+import android.debug.AdbTransportType;
+
/** @hide */
interface IAdbTransport {
- void onAdbEnabled(boolean enabled);
+ void onAdbEnabled(boolean enabled, in AdbTransportType type);
}
diff --git a/core/java/android/debug/PairDevice.java b/core/java/android/debug/PairDevice.java
new file mode 100644
index 0000000..2d5b446
--- /dev/null
+++ b/core/java/android/debug/PairDevice.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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.debug;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains information about the client in an ADB connection.
+ * @hide
+ */
+@Immutable
+public class PairDevice implements Parcelable {
+ /**
+ * The human-readable name of the device.
+ */
+ @NonNull private final String mName;
+
+ /**
+ * The device's guid.
+ */
+ @NonNull private final String mGuid;
+
+ /**
+ * Indicates whether the device is currently connected to adbd.
+ */
+ private final boolean mConnected;
+
+ public PairDevice(@NonNull String name, @NonNull String guid, boolean connected) {
+ Preconditions.checkStringNotEmpty(name);
+ Preconditions.checkStringNotEmpty(guid);
+ mName = name;
+ mGuid = guid;
+ mConnected = connected;
+ }
+
+ /**
+ * @return the device name.
+ */
+ @NonNull
+ public String getDeviceName() {
+ return mName;
+ }
+
+ /**
+ * @return the device GUID.
+ */
+ @NonNull
+ public String getGuid() {
+ return mGuid;
+ }
+
+ /**
+ * @return the adb connection state of the device.
+ */
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mGuid);
+ dest.writeBoolean(mConnected);
+ }
+
+ /**
+ * @return Human-readable info about the object.
+ */
+ @Override
+ public String toString() {
+ return "\n" + mName + "\n" + mGuid + "\n" + mConnected;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<PairDevice> CREATOR =
+ new Creator<PairDevice>() {
+ @Override
+ public PairDevice createFromParcel(Parcel source) {
+ return new PairDevice(source.readString(), source.readString(),
+ source.readBoolean());
+ }
+
+ @Override
+ public PairDevice[] newArray(int size) {
+ return new PairDevice[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 24d9311..cc0c1a30 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -688,17 +688,19 @@
* <tr><th colspan="5">Concurrent stream guaranteed configurations</th></tr>
* <tr><th colspan="2" id="rb">Target 1</th><th colspan="2" id="rb">Target 2</th><th rowspan="2">Sample use case(s)</th> </tr>
* <tr><th>Type</th><th id="rb">Max size</th><th>Type</th><th id="rb">Max size</th> </tr>
- * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr>
- * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
- * <tr> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>In-app video / processing with preview.</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV }</td><td id="rb">{@code MAXIMUM}</td> <td>Standard Recording.</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>In-app video / image processing.</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>In-app viewfinder analysis.</td> </tr>
+ * <tr> <td>{@code JPEG}</td><td id="rb">{@code s1440p}</td> <td colspan="2" id="rb"></td> <td>No viewfinder still image capture.</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s720p}</td> <td>{@code JPEG}</td><td id="rb">{@code s1440p}</td> <td> Standard still imaging.</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s720p}</td> <td>{@code YUV / PRIV }</td><td id="rb">{@code s1440p}</td> <td>In-app video / processing with preview.</td> </tr>
* </table><br>
* </p>
*
- * <p> For guaranteed concurrent stream configurations, MAXIMUM refers to the camera device's
- * resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * <p> For guaranteed concurrent stream configurations:</p>
+ * <p> s720p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 720p(1280X720) whichever is lower. </p>
+ * <p> s1440p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * 1440p(1920X1440) whichever is lower. </p>
* <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
* includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
* supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 17c83f3..743ce7b 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -140,6 +140,11 @@
* <p>The set of combinations doesn't contain physical cameras that can only be used as
* part of a logical multi-camera device.</p>
*
+ * <p> If a new camera id becomes available through
+ * {@link AvailabilityCallback#onCameraUnavailable(String)}, clients can call
+ * this method to check if new combinations of camera ids which can stream concurrently are
+ * available.
+ *
* @return The set of combinations of currently connected camera devices, that may have
* sessions configured concurrently. The set of combinations will be empty if no such
* combinations are supported by the camera subsystem.
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 41e1443..f0fab6a 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -267,7 +267,7 @@
mStreamsInformation.hashCode());
}
- private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p }
+ private static enum SizeThreshold { VGA, PREVIEW, RECORD, MAXIMUM, s720p, s1440p }
private static enum ReprocessType { NONE, PRIVATE, YUV }
private static final class StreamTemplate {
public int mFormat;
@@ -651,23 +651,38 @@
private static StreamCombinationTemplate sConcurrentStreamCombinations[] = {
new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p) },
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p) },
"In-app video / image processing"),
new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p) },
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p) },
"preview / preview to GPU"),
new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p) },
+ "No view-finder still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)},
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
+ "Two-input in app video / image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
+ "High resolution video recording with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p)},
+ "In-app video / image processing with preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p)},
"In-app video / image processing with preview"),
new StreamCombinationTemplate(new StreamTemplate [] {
new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
- new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p)},
- "In-app video / image processing with preview"),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
+ "Standard stil image capture"),
new StreamCombinationTemplate(new StreamTemplate [] {
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p),
- new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s720p)},
- "Standard Recording"),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s720p),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.s1440p)},
+ "Standard still image capture"),
};
/**
@@ -720,6 +735,7 @@
+ " cannot have mandatory concurrent streams");
}
Size size720p = new Size(1280, 720);
+ Size size1440p = new Size(1920, 1440);
ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
new ArrayList<MandatoryStreamCombination>();
@@ -732,8 +748,16 @@
for (StreamTemplate template : combTemplate.mStreamTemplates) {
MandatoryStreamInformation streamInfo;
List<Size> sizes = new ArrayList<Size>();
+ Size formatSize = null;
+ switch (template.mSizeThreshold) {
+ case s1440p:
+ formatSize = size1440p;
+ break;
+ default:
+ formatSize = size720p;
+ }
Size sizeChosen =
- getMinSize(size720p,
+ getMinSize(formatSize,
getMaxSize(mStreamConfigMap.getOutputSizes(template.mFormat)));
sizes.add(sizeChosen);
try {
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 6ad7fae..ce26cb0 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -28,21 +28,21 @@
* @hide
*/
public final class DeviceProductInfo implements Parcelable {
- final private String mName;
- final private String mManufacturerPnpId;
- final private String mProductId;
- final private Integer mModelYear;
- final private ManufactureDate mManufactureDate;
+ private final String mName;
+ private final String mManufacturerPnpId;
+ private final String mProductId;
+ private final Integer mModelYear;
+ private final ManufactureDate mManufactureDate;
public DeviceProductInfo(
String name,
String manufacturerPnpId,
- String productCode,
+ String productId,
Integer modelYear,
ManufactureDate manufactureDate) {
this.mName = name;
this.mManufacturerPnpId = manufacturerPnpId;
- this.mProductId = productCode;
+ this.mProductId = productId;
this.mModelYear = modelYear;
this.mManufactureDate = manufactureDate;
}
@@ -158,8 +158,8 @@
* @hide
*/
public static class ManufactureDate implements Parcelable {
- final private Integer mWeek;
- final private Integer mYear;
+ private final Integer mWeek;
+ private final Integer mYear;
public ManufactureDate(Integer week, Integer year) {
mWeek = week;
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 3a0660d..9b9889e 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -97,8 +97,10 @@
}
@Override // binder call
- public void onAuthenticationSucceeded(long deviceId, Face face, int userId) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget();
+ public void onAuthenticationSucceeded(long deviceId, Face face, int userId,
+ boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
+ face).sendToTarget();
}
@Override // binder call
@@ -814,6 +816,7 @@
private Face mFace;
private CryptoObject mCryptoObject;
private int mUserId;
+ private boolean mIsStrongBiometric;
/**
* Authentication result
@@ -822,10 +825,12 @@
* @param face the recognized face data, if allowed.
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
+ public AuthenticationResult(CryptoObject crypto, Face face, int userId,
+ boolean isStrongBiometric) {
mCryptoObject = crypto;
mFace = face;
mUserId = userId;
+ mIsStrongBiometric = isStrongBiometric;
}
/**
@@ -857,6 +862,16 @@
public int getUserId() {
return mUserId;
}
+
+ /**
+ * Check whether the strength of the face modality associated with this operation is strong
+ * (i.e. not weak or convenience).
+ *
+ * @hide
+ */
+ public boolean isStrongBiometric() {
+ return mIsStrongBiometric;
+ }
}
/**
@@ -1056,7 +1071,8 @@
msg.arg2 /* vendorCode */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
- sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
+ sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */,
+ msg.arg2 == 1 /* isStrongBiometric */);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
@@ -1133,10 +1149,10 @@
}
}
- private void sendAuthenticatedSucceeded(Face face, int userId) {
+ private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, face, userId);
+ new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 8ba2473..6318274 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -110,4 +110,7 @@
void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName);
void userActivity();
+
+ // Initialize the OEM configured biometric strength
+ void initConfiguredStrength(int strength);
}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 10f9c43..7582308 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -24,7 +24,8 @@
oneway interface IFaceServiceReceiver {
void onEnrollResult(long deviceId, int faceId, int remaining);
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
- void onAuthenticationSucceeded(long deviceId, in Face face, int userId);
+ void onAuthenticationSucceeded(long deviceId, in Face face, int userId,
+ boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int faceId, int remaining);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index f301a5c..dc8ea5e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -176,6 +176,7 @@
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
private int mUserId;
+ private boolean mIsStrongBiometric;
/**
* Authentication result
@@ -184,10 +185,12 @@
* @param fingerprint the recognized fingerprint data, if allowed.
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId) {
+ public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId,
+ boolean isStrongBiometric) {
mCryptoObject = crypto;
mFingerprint = fingerprint;
mUserId = userId;
+ mIsStrongBiometric = isStrongBiometric;
}
/**
@@ -211,6 +214,15 @@
* @hide
*/
public int getUserId() { return mUserId; }
+
+ /**
+ * Check whether the strength of the fingerprint modality associated with this operation is
+ * strong (i.e. not weak or convenience).
+ * @hide
+ */
+ public boolean isStrongBiometric() {
+ return mIsStrongBiometric;
+ }
};
/**
@@ -833,7 +845,8 @@
msg.arg2 /* vendorCode */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
- sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
+ sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */,
+ msg.arg2 == 1 /* isStrongBiometric */);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
@@ -890,10 +903,10 @@
}
}
- private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
+ private void sendAuthenticatedSucceeded(Fingerprint fp, int userId, boolean isStrongBiometric) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
+ new AuthenticationResult(mCryptoObject, fp, userId, isStrongBiometric);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1078,8 +1091,10 @@
}
@Override // binder call
- public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
+ public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId,
+ boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
+ fp).sendToTarget();
}
@Override // binder call
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index f2ffd08d..0285448 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -113,4 +113,7 @@
// Removes a callback set by addClientActiveCallback
void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
+
+ // Initialize the OEM configured biometric strength
+ void initConfiguredStrength(int strength);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index cf1c94e..4412cee 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -24,7 +24,8 @@
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
- void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId);
+ void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId,
+ boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int fingerId, int groupId, int remaining);
diff --git a/core/java/android/hardware/iris/IIrisService.aidl b/core/java/android/hardware/iris/IIrisService.aidl
index 8cf3c13..5ef0a0c 100644
--- a/core/java/android/hardware/iris/IIrisService.aidl
+++ b/core/java/android/hardware/iris/IIrisService.aidl
@@ -21,4 +21,6 @@
* @hide
*/
interface IIrisService {
-}
\ No newline at end of file
+ // Initialize the OEM configured biometric strength
+ void initConfiguredStrength(int strength);
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 086db10..d16f070 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
package android.hardware.usb;
import android.Manifest;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -337,12 +338,14 @@
* Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_MTP = GadgetFunction.MTP;
/**
* Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_PTP = GadgetFunction.PTP;
/**
@@ -356,24 +359,28 @@
* Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
/**
* Code for the accessory usb function.
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
/**
* Code for the audio source usb function.
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
/**
* Code for the adb usb function.
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_ADB = GadgetFunction.ADB;
/**
@@ -399,6 +406,20 @@
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
}
+ /** @hide */
+ @LongDef(flag = true, prefix = { "FUNCTION_" }, value = {
+ FUNCTION_NONE,
+ FUNCTION_MTP,
+ FUNCTION_PTP,
+ FUNCTION_RNDIS,
+ FUNCTION_MIDI,
+ FUNCTION_ACCESSORY,
+ FUNCTION_AUDIO_SOURCE,
+ FUNCTION_ADB,
+ FUNCTION_NCM,
+ })
+ public @interface UsbFunctionMode {}
+
private final Context mContext;
private final IUsbManager mService;
@@ -721,7 +742,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_USB)
- public void setCurrentFunctions(long functions) {
+ public void setCurrentFunctions(@UsbFunctionMode long functions) {
try {
mService.setCurrentFunctions(functions);
} catch (RemoteException e) {
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 2a441de..f0b1eaa 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -19,7 +19,6 @@
import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -29,7 +28,6 @@
import android.os.ResultReceiver;
import android.util.Log;
import android.view.InputChannel;
-import android.view.autofill.AutofillId;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputConnection;
@@ -46,6 +44,7 @@
import com.android.internal.view.IInputMethod;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.internal.view.InputConnectionWrapper;
import java.io.FileDescriptor;
@@ -233,8 +232,9 @@
return;
case DO_CREATE_INLINE_SUGGESTIONS_REQUEST:
args = (SomeArgs) msg.obj;
- inputMethod.onCreateInlineSuggestionsRequest((ComponentName) args.arg1,
- (AutofillId) args.arg2, (IInlineSuggestionsRequestCallback) args.arg3);
+ inputMethod.onCreateInlineSuggestionsRequest(
+ (InlineSuggestionsRequestInfo) args.arg1,
+ (IInlineSuggestionsRequestCallback) args.arg2);
return;
}
@@ -279,11 +279,10 @@
@BinderThread
@Override
- public void onCreateInlineSuggestionsRequest(ComponentName componentName, AutofillId autofillId,
+ public void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback cb) {
mCaller.executeOrSendMessage(
- mCaller.obtainMessageOOO(DO_CREATE_INLINE_SUGGESTIONS_REQUEST, componentName,
- autofillId, cb));
+ mCaller.obtainMessageOO(DO_CREATE_INLINE_SUGGESTIONS_REQUEST, requestInfo, cb));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b1aa67e..20a4ab3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -34,7 +34,6 @@
import android.app.ActivityManager;
import android.app.Dialog;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -74,7 +73,6 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
-import android.view.autofill.AutofillId;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
@@ -99,6 +97,7 @@
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -526,12 +525,13 @@
*/
@MainThread
@Override
- public void onCreateInlineSuggestionsRequest(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
+ public void onCreateInlineSuggestionsRequest(
+ @NonNull InlineSuggestionsRequestInfo requestInfo,
+ @NonNull IInlineSuggestionsRequestCallback cb) {
if (DEBUG) {
Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
}
- handleOnCreateInlineSuggestionsRequest(componentName, cb);
+ handleOnCreateInlineSuggestionsRequest(requestInfo, cb);
}
/**
@@ -742,11 +742,14 @@
// TODO(b/137800469): Add detailed docs explaining the inline suggestions process.
/**
- * Returns an {@link InlineSuggestionsRequest} to be sent to Autofill.
+ * This method should be implemented by subclass which supports displaying autofill inline
+ * suggestion.
*
- * <p>Should be implemented by subclasses.</p>
+ * @param uiExtras the extras that contain the UI renderer related information
+ * @return an {@link InlineSuggestionsRequest} to be sent to Autofill.
*/
- public @Nullable InlineSuggestionsRequest onCreateInlineSuggestionsRequest() {
+ @Nullable
+ public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) {
return null;
}
@@ -764,7 +767,8 @@
}
@MainThread
- private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
+ private void handleOnCreateInlineSuggestionsRequest(
+ @NonNull InlineSuggestionsRequestInfo requestInfo,
@NonNull IInlineSuggestionsRequestCallback callback) {
if (!mInputStarted) {
try {
@@ -779,8 +783,9 @@
if (mInlineSuggestionSession != null) {
mInlineSuggestionSession.invalidateSession();
}
- mInlineSuggestionSession = new InlineSuggestionSession(componentName, callback,
- this::getEditorInfoPackageName, this::onCreateInlineSuggestionsRequest,
+ mInlineSuggestionSession = new InlineSuggestionSession(requestInfo.getComponentName(),
+ callback, this::getEditorInfoPackageName,
+ () -> onCreateInlineSuggestionsRequest(requestInfo.getUiExtras()),
this::getHostInputToken, this::onInlineSuggestionsResponse);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ea26ede..62eff45 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1279,7 +1279,8 @@
@UnsupportedAppUsage
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
try {
- return mService.getDefaultNetworkCapabilitiesForUser(userId);
+ return mService.getDefaultNetworkCapabilitiesForUser(
+ userId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1361,7 +1362,7 @@
@Nullable
public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
- return mService.getNetworkCapabilities(network);
+ return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4039,10 +4040,9 @@
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
- final String callingPackageName = mContext.getOpPackageName();
try {
mService.pendingRequestForNetwork(
- request.networkCapabilities, operation, callingPackageName);
+ request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4154,10 +4154,9 @@
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
- final String callingPackageName = mContext.getOpPackageName();
try {
mService.pendingListenForNetwork(
- request.networkCapabilities, operation, callingPackageName);
+ request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1c7628f..d84d05d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -60,7 +60,8 @@
NetworkInfo[] getAllNetworkInfo();
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
- NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
+ NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+ int userId, String callingPackageName);
boolean isNetworkSupported(int networkType);
@@ -69,7 +70,7 @@
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
- NetworkCapabilities getNetworkCapabilities(in Network network);
+ NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
@UnsupportedAppUsage
NetworkState[] getAllNetworkState();
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 42b4da1..f19a341 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,7 +25,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Process;
import android.security.Credentials;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnProfile;
@@ -59,6 +62,11 @@
* Exchange, Version 2 (IKEv2)</a>
*/
public final class Ikev2VpnProfile extends PlatformVpnProfile {
+ /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
+ public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
+ /** Prefix for when a Private Key is stored directly in the profile @hide */
+ public static final String PREFIX_INLINE = "INLINE:";
+
private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
private static final String EMPTY_CERT = "";
@@ -339,7 +347,8 @@
break;
case TYPE_IKEV2_IPSEC_RSA:
profile.ipsecUserCert = certificateToPemString(mUserCert);
- profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
+ profile.ipsecSecret =
+ PREFIX_INLINE + encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
profile.ipsecCaCert =
mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
break;
@@ -360,6 +369,22 @@
@NonNull
public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
throws IOException, GeneralSecurityException {
+ return fromVpnProfile(profile, null);
+ }
+
+ /**
+ * Builds the Ikev2VpnProfile from the given profile.
+ *
+ * @param profile the source VpnProfile to build from
+ * @param keyStore the Android Keystore instance to use to retrieve the private key, or null if
+ * the private key is PEM-encoded into the profile.
+ * @return The IKEv2/IPsec VPN profile
+ * @hide
+ */
+ @NonNull
+ public static Ikev2VpnProfile fromVpnProfile(
+ @NonNull VpnProfile profile, @Nullable KeyStore keyStore)
+ throws IOException, GeneralSecurityException {
final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
builder.setProxy(profile.proxy);
builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
@@ -378,8 +403,21 @@
builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
break;
case TYPE_IKEV2_IPSEC_RSA:
+ final PrivateKey key;
+ if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
+ Objects.requireNonNull(keyStore, "Missing Keystore for aliased PrivateKey");
+
+ final String alias =
+ profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
+ key = AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
+ keyStore, alias, Process.myUid());
+ } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
+ key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
+ } else {
+ throw new IllegalArgumentException("Invalid RSA private key prefix");
+ }
+
final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
- final PrivateKey key = getPrivateKey(profile.ipsecSecret);
final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
builder.setAuthDigitalSignature(userCert, key, serverRootCa);
break;
@@ -391,6 +429,39 @@
}
/**
+ * Validates that the VpnProfile is acceptable for the purposes of an Ikev2VpnProfile.
+ *
+ * @hide
+ */
+ public static boolean isValidVpnProfile(@NonNull VpnProfile profile) {
+ if (profile.server.isEmpty() || profile.ipsecIdentifier.isEmpty()) {
+ return false;
+ }
+
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ if (profile.username.isEmpty() || profile.password.isEmpty()) {
+ return false;
+ }
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ if (profile.ipsecSecret.isEmpty()) {
+ return false;
+ }
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ if (profile.ipsecSecret.isEmpty() || profile.ipsecUserCert.isEmpty()) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Converts a X509 Certificate to a PEM-formatted string.
*
* <p>Must be public due to runtime-package restrictions.
@@ -432,7 +503,6 @@
/** @hide */
@NonNull
- @VisibleForTesting(visibility = Visibility.PRIVATE)
public static String encodeForIpsecSecret(@NonNull byte[] secret) {
checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f8b51dd..83f9980 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -830,6 +830,23 @@
* <p>This field keeps track of the UID of the app that created this network and is in charge of
* its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
* VPN, or Carrier Service app managing a cellular data connection.
+ *
+ * <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be
+ * reset to Process.INVALID_UID unless all the following conditions are met:
+ *
+ * <ol>
+ * <li>The destination app is the network owner
+ * <li>The destination app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The user's location toggle is on
+ * </ol>
+ *
+ * This is because the owner UID is location-sensitive. The apps that request a network could
+ * know where the device is if they can tell for sure the system has connected to the network
+ * they requested.
+ *
+ * <p>This is populated by the network agents and for the NetworkCapabilities instance sent by
+ * an app to the System Server, the value MUST be reset to Process.INVALID_UID by the system
+ * server.
*/
private int mOwnerUid = Process.INVALID_UID;
@@ -842,7 +859,16 @@
}
/**
- * Retrieves the UID of the owner app.
+ * Retrieves the UID of the app that owns this network.
+ *
+ * <p>For user privacy reasons, this field will only be populated if:
+ *
+ * <ol>
+ * <li>The calling app is the network owner
+ * <li>The calling app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The user's location toggle is on
+ * </ol>
+ *
*/
public int getOwnerUid() {
return mOwnerUid;
@@ -880,8 +906,9 @@
* @param administratorUids the UIDs to be set as administrators of this Network.
* @hide
*/
+ @NonNull
@SystemApi
- public @NonNull NetworkCapabilities setAdministratorUids(
+ public NetworkCapabilities setAdministratorUids(
@NonNull final List<Integer> administratorUids) {
mAdministratorUids.clear();
mAdministratorUids.addAll(administratorUids);
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index be22458..d60820e 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -16,6 +16,8 @@
package android.os;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static java.nio.charset.StandardCharsets.UTF_8;
import android.annotation.NonNull;
@@ -33,6 +35,7 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -614,7 +617,8 @@
// On TV, reboot quiescently if the screen is off
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
- if (context.getDisplay().getState() != Display.STATE_ON) {
+ DisplayManager dm = context.getSystemService(DisplayManager.class);
+ if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) {
reason += ",quiescent";
}
}
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 3faaff7..e8af564 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -42,6 +42,7 @@
import android.os.strictmode.ExplicitGcViolation;
import android.os.strictmode.FileUriExposedViolation;
import android.os.strictmode.ImplicitDirectBootViolation;
+import android.os.strictmode.IncorrectContextUseViolation;
import android.os.strictmode.InstanceCountViolation;
import android.os.strictmode.IntentReceiverLeakedViolation;
import android.os.strictmode.LeakedClosableViolation;
@@ -250,6 +251,7 @@
DETECT_VM_UNTAGGED_SOCKET,
DETECT_VM_NON_SDK_API_USAGE,
DETECT_VM_IMPLICIT_DIRECT_BOOT,
+ DETECT_VM_INCORRECT_CONTEXT_USE,
PENALTY_GATHER,
PENALTY_LOG,
PENALTY_DIALOG,
@@ -289,6 +291,8 @@
private static final int DETECT_VM_IMPLICIT_DIRECT_BOOT = 1 << 10;
/** @hide */
private static final int DETECT_VM_CREDENTIAL_PROTECTED_WHILE_LOCKED = 1 << 11;
+ /** @hide */
+ private static final int DETECT_VM_INCORRECT_CONTEXT_USE = 1 << 12;
/** @hide */
private static final int DETECT_VM_ALL = 0x0000ffff;
@@ -871,6 +875,9 @@
if (targetSdk >= Build.VERSION_CODES.Q) {
detectCredentialProtectedWhileLocked();
}
+ if (targetSdk >= Build.VERSION_CODES.R) {
+ detectIncorrectContextUse();
+ }
// TODO: Decide whether to detect non SDK API usage beyond a certain API level.
// TODO: enable detectImplicitDirectBoot() once system is less noisy
@@ -1028,6 +1035,32 @@
}
/**
+ * Detect attempts to invoke a method on a {@link Context} that is not suited for such
+ * operation.
+ * <p>An example of this is trying to obtain an instance of visual service (e.g.
+ * {@link android.view.WindowManager}) from a non-visual {@link Context}. This is not
+ * allowed, since a non-visual {@link Context} is not adjusted to any visual area, and
+ * therefore can report incorrect metrics or resources.
+ * @see Context#getDisplay()
+ * @see Context#getSystemService(String)
+ * @hide
+ */
+ @TestApi
+ public @NonNull Builder detectIncorrectContextUse() {
+ return enable(DETECT_VM_INCORRECT_CONTEXT_USE);
+ }
+
+ /**
+ * Disable detection of incorrect context use.
+ * TODO(b/149790106): Fix usages and remove.
+ * @hide
+ */
+ @TestApi
+ public @NonNull Builder permitIncorrectContextUse() {
+ return disable(DETECT_VM_INCORRECT_CONTEXT_USE);
+ }
+
+ /**
* Crashes the whole process on violation. This penalty runs at the end of all enabled
* penalties so you'll still get your logging or other violations before the process
* dies.
@@ -2057,6 +2090,11 @@
}
/** @hide */
+ public static boolean vmIncorrectContextUseEnabled() {
+ return (sVmPolicy.mask & DETECT_VM_INCORRECT_CONTEXT_USE) != 0;
+ }
+
+ /** @hide */
public static void onSqliteObjectLeaked(String message, Throwable originStack) {
onVmPolicyViolation(new SqliteObjectLeakedViolation(message, originStack));
}
@@ -2130,6 +2168,11 @@
onVmPolicyViolation(new ImplicitDirectBootViolation());
}
+ /** @hide */
+ public static void onIncorrectContextUsed(String message, Throwable originStack) {
+ onVmPolicyViolation(new IncorrectContextUseViolation(message, originStack));
+ }
+
/** Assume locked until we hear otherwise */
private static volatile boolean sUserKeyUnlocked = false;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 84fd580..0f2060a 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -63,6 +63,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Manages users and user details on a multi-user system. There are two major categories of
@@ -2717,10 +2718,11 @@
Manifest.permission.CREATE_USERS})
@UserHandleAware
public @Nullable UserHandle createProfile(@NonNull String name, @NonNull String userType,
- @Nullable String[] disallowedPackages) throws UserOperationException {
+ @NonNull Set<String> disallowedPackages) throws UserOperationException {
try {
return mService.createProfileForUserWithThrow(name, userType, 0,
- mUserId, disallowedPackages).getUserHandle();
+ mUserId, disallowedPackages.toArray(
+ new String[disallowedPackages.size()])).getUserHandle();
} catch (ServiceSpecificException e) {
return returnNullOrThrowUserOperationException(e,
mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R);
@@ -3354,19 +3356,46 @@
}
/**
- * Returns a list of ids for profiles associated with the context user including the user
- * itself.
+ * Returns a list of ids for enabled profiles associated with the context user including the
+ * user itself.
*
- * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
* @return A non-empty list of UserHandles associated with the calling user.
- *
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS}, conditional = true)
@UserHandleAware
- public @NonNull List<UserHandle> getUserProfiles(boolean enabledOnly) {
+ public @NonNull List<UserHandle> getEnabledProfiles() {
+ return getProfiles(true);
+ }
+
+ /**
+ * Returns a list of ids for all profiles associated with the context user including the user
+ * itself.
+ *
+ * @return A non-empty list of UserHandles associated with the calling user.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
+ @UserHandleAware
+ public @NonNull List<UserHandle> getAllProfiles() {
+ return getProfiles(false);
+ }
+
+ /**
+ * Returns a list of ids for profiles associated with the context user including the user
+ * itself.
+ *
+ * @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
+ * @return A non-empty list of UserHandles associated with the calling user.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}, conditional = true)
+ @UserHandleAware
+ private @NonNull List<UserHandle> getProfiles(boolean enabledOnly) {
final int[] userIds = getProfileIds(mUserId, enabledOnly);
final List<UserHandle> result = new ArrayList<>(userIds.length);
for (int userId : userIds) {
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index f4e1f96..dea495b 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -434,6 +434,11 @@
signature = V4Signature.readFrom(input);
}
+ if (!signature.isVersionSupported()) {
+ throw new IOException("v4 signature version " + signature.version
+ + " is not supported");
+ }
+
final byte[] rootHash = signature.verityRootHash;
final byte[] additionalData = signature.v3Digest;
final byte[] pkcs7Signature = signature.pkcs7SignatureBlock;
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 6516917..17adfc8 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -31,7 +31,9 @@
*/
public class V4Signature {
public static final String EXT = ".idsig";
+ public static final int SUPPORTED_VERSION = 1;
+ public final int version;
public final byte[] verityRootHash;
public final byte[] v3Digest;
public final byte[] pkcs7SignatureBlock;
@@ -71,20 +73,27 @@
}
}
+ boolean isVersionSupported() {
+ return this.version == SUPPORTED_VERSION;
+ }
+
static V4Signature readFrom(DataInputStream stream) throws IOException {
+ final int version = stream.readInt();
byte[] verityRootHash = readBytes(stream);
byte[] v3Digest = readBytes(stream);
byte[] pkcs7SignatureBlock = readBytes(stream);
- return new V4Signature(verityRootHash, v3Digest, pkcs7SignatureBlock);
+ return new V4Signature(version, verityRootHash, v3Digest, pkcs7SignatureBlock);
}
- V4Signature(byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) {
+ V4Signature(int version, byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) {
+ this.version = version;
this.verityRootHash = verityRootHash;
this.v3Digest = v3Digest;
this.pkcs7SignatureBlock = pkcs7SignatureBlock;
}
void writeTo(DataOutputStream stream) throws IOException {
+ stream.writeInt(this.version);
writeBytes(stream, this.verityRootHash);
writeBytes(stream, this.v3Digest);
writeBytes(stream, this.pkcs7SignatureBlock);
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8d04df0..1454aac 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -29,6 +29,7 @@
import static android.app.AppOpsManager.OP_WRITE_MEDIA_VIDEO;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.PER_USER_RANGE;
import android.annotation.BytesLong;
import android.annotation.CallbackExecutor;
@@ -2324,17 +2325,34 @@
private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone";
- // Matches AID_MEDIA_RW in android_filesystem_config.h
- private static final int QUOTA_PROJECT_ID_MEDIA_NONE = 1023;
+ // Project IDs below must match android_projectid_config.h
+ /**
+ * Default project ID for files on external storage
+ *
+ * {@hide}
+ */
+ public static final int PROJECT_ID_EXT_DEFAULT = 1000;
- // Matches AID_MEDIA_IMAGE in android_filesystem_config.h
- private static final int QUOTA_PROJECT_ID_MEDIA_IMAGE = 1057;
+ /**
+ * project ID for audio files on external storage
+ *
+ * {@hide}
+ */
+ public static final int PROJECT_ID_EXT_MEDIA_AUDIO = 1001;
- // Matches AID_MEDIA_AUDIO in android_filesystem_config.h
- private static final int QUOTA_PROJECT_ID_MEDIA_AUDIO = 1055;
+ /**
+ * project ID for video files on external storage
+ *
+ * {@hide}
+ */
+ public static final int PROJECT_ID_EXT_MEDIA_VIDEO = 1002;
- // Matches AID_MEDIA_VIDEO in android_filesystem_config.h
- private static final int QUOTA_PROJECT_ID_MEDIA_VIDEO = 1056;
+ /**
+ * project ID for image files on external storage
+ *
+ * {@hide}
+ */
+ public static final int PROJECT_ID_EXT_MEDIA_IMAGE = 1003;
/**
* Constant for use with
@@ -2388,6 +2406,11 @@
private static native boolean setQuotaProjectId(String path, long projectId);
+ private static long getProjectIdForUser(int userId, int projectId) {
+ // Much like UserHandle.getUid(), store the user ID in the upper bits
+ return userId * PER_USER_RANGE + projectId;
+ }
+
/**
* Let StorageManager know that the quota type for a file on external storage should
* be updated. Android tracks quotas for various media types. Consequently, this should be
@@ -2417,18 +2440,27 @@
@QuotaType int quotaType) throws IOException {
long projectId;
final String filePath = path.getCanonicalPath();
+ final StorageVolume volume = getStorageVolume(path);
+ if (volume == null) {
+ throw new IllegalStateException("Failed to update quota type for " + filePath);
+ }
+
+ final int userId = volume.getOwner().getIdentifier();
+ if (userId < 0) {
+ throw new IllegalStateException("Failed to update quota type for " + filePath);
+ }
switch (quotaType) {
case QUOTA_TYPE_MEDIA_NONE:
- projectId = QUOTA_PROJECT_ID_MEDIA_NONE;
+ projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_DEFAULT);
break;
case QUOTA_TYPE_MEDIA_AUDIO:
- projectId = QUOTA_PROJECT_ID_MEDIA_AUDIO;
+ projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_AUDIO);
break;
case QUOTA_TYPE_MEDIA_VIDEO:
- projectId = QUOTA_PROJECT_ID_MEDIA_VIDEO;
+ projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_VIDEO);
break;
case QUOTA_TYPE_MEDIA_IMAGE:
- projectId = QUOTA_PROJECT_ID_MEDIA_IMAGE;
+ projectId = getProjectIdForUser(userId, PROJECT_ID_EXT_MEDIA_IMAGE);
break;
default:
throw new IllegalArgumentException("Invalid quota type: " + quotaType);
diff --git a/core/java/android/os/strictmode/IncorrectContextUseViolation.java b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
new file mode 100644
index 0000000..647db17
--- /dev/null
+++ b/core/java/android/os/strictmode/IncorrectContextUseViolation.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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.os.strictmode;
+
+import android.content.Context;
+
+/**
+ * Incorrect usage of {@link Context}, such as obtaining a visual service from non-visual
+ * {@link Context} instance.
+ * @see Context#getSystemService(String)
+ * @see Context#getDisplayNoVerify()
+ * @hide
+ */
+public final class IncorrectContextUseViolation extends Violation {
+
+ /** @hide */
+ public IncorrectContextUseViolation(String message, Throwable originStack) {
+ super(message);
+ initCause(originStack);
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9511152..b898271 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9275,11 +9275,17 @@
public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user";
/**
- * Whether ADB is enabled.
+ * Whether ADB over USB is enabled.
*/
public static final String ADB_ENABLED = "adb_enabled";
/**
+ * Whether ADB over Wifi is enabled.
+ * @hide
+ */
+ public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled";
+
+ /**
* Whether Views are allowed to save their attribute data.
* @hide
*/
@@ -11509,6 +11515,7 @@
* exempted_sync_duration (long)
* system_interaction_duration (long)
* initial_foreground_service_start_duration (long)
+ * cross_profile_apps_share_standby_buckets (boolean)
* </pre>
*
* <p>
@@ -14325,7 +14332,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS)
public static void registerMonitorCallback(@NonNull ContentResolver resolver,
@NonNull RemoteCallback callback) {
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 4aafb63..29069e7 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -25,6 +25,7 @@
import android.app.slice.Slice;
import android.content.Intent;
import android.graphics.PixelFormat;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -118,6 +119,14 @@
}
/**
+ * Returns the metadata about the renderer. Returns {@code null} if no metadata is provided.
+ */
+ @Nullable
+ public Bundle onGetInlineSuggestionsRendererInfo() {
+ return null;
+ }
+
+ /**
* Renders the slice into a view.
*/
@Nullable
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index cb20db9..b23d0cd 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -15,11 +15,14 @@
*/
package android.service.controls;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -51,6 +54,20 @@
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_CONTROLS =
"android.service.controls.ControlsProviderService";
+
+ /**
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ADD_CONTROL =
+ "android.service.controls.action.ADD_CONTROL";
+
+ /**
+ * @hide
+ */
+ public static final String EXTRA_CONTROL =
+ "android.service.controls.extra.CONTROL";
+
/**
* @hide
*/
@@ -325,6 +342,33 @@
}
}
+ /**
+ * Request SystemUI to prompt the user to add a control to favorites.
+ *
+ * @param context A context
+ * @param componentName Component name of the {@link ControlsProviderService}
+ * @param control A stateless control to show to the user
+ */
+ public static void requestAddControl(@NonNull Context context,
+ @NonNull ComponentName componentName,
+ @NonNull Control control) {
+ Preconditions.checkNotNull(context);
+ Preconditions.checkNotNull(componentName);
+ Preconditions.checkNotNull(control);
+ final ComponentName sysuiComponent = ComponentName.unflattenFromString(
+ context.getResources().getString(
+ com.android.internal.R.string.config_systemUIServiceComponent));
+ Intent intent = new Intent(ACTION_ADD_CONTROL);
+ intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName);
+ intent.setPackage(sysuiComponent.getPackageName());
+ if (isStatelessControl(control)) {
+ intent.putExtra(EXTRA_CONTROL, control);
+ } else {
+ intent.putExtra(EXTRA_CONTROL, new Control.StatelessBuilder(control).build());
+ }
+ context.sendBroadcast(intent, Manifest.permission.BIND_CONTROLS);
+ }
+
private static class SubscriptionAdapter extends IControlsSubscription.Stub {
final Subscription mSubscription;
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index c9dc05b..ead4e46 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -16,6 +16,8 @@
package android.util;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -62,7 +64,7 @@
private Exception mLastWtf;
// Layout of event log entry received from Android logger.
- // see system/core/include/log/log.h
+ // see system/core/liblog/include/log/log_read.h
private static final int LENGTH_OFFSET = 0;
private static final int HEADER_SIZE_OFFSET = 2;
private static final int PROCESS_OFFSET = 4;
@@ -73,7 +75,7 @@
// Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
private static final int V1_PAYLOAD_START = 20;
- private static final int DATA_OFFSET = 4;
+ private static final int TAG_LENGTH = 4;
// Value types
private static final byte INT_TYPE = 0;
@@ -121,26 +123,26 @@
/** @return the type tag code of the entry */
public int getTag() {
- int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
- if (offset == 0) {
- offset = V1_PAYLOAD_START;
- }
- return mBuffer.getInt(offset);
+ return mBuffer.getInt(getHeaderSize());
}
+ private int getHeaderSize() {
+ int length = mBuffer.getShort(HEADER_SIZE_OFFSET);
+ if (length != 0) {
+ return length;
+ }
+ return V1_PAYLOAD_START;
+ }
/** @return one of Integer, Long, Float, String, null, or Object[] of same. */
public synchronized Object getData() {
try {
- int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
- if (offset == 0) {
- offset = V1_PAYLOAD_START;
- }
+ int offset = getHeaderSize();
mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
- if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
+ if ((offset + TAG_LENGTH) >= mBuffer.limit()) {
// no payload
return null;
}
- mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
+ mBuffer.position(offset + TAG_LENGTH); // Just after the tag.
return decodeObject();
} catch (IllegalArgumentException e) {
Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
@@ -153,6 +155,28 @@
}
}
+ /**
+ * Construct a new EventLog object from the current object, copying all log metadata
+ * but replacing the actual payload with the content provided.
+ * @hide
+ */
+ public Event withNewData(@Nullable Object object) {
+ byte[] payload = encodeObject(object);
+ if (payload.length > 65535 - TAG_LENGTH) {
+ throw new IllegalArgumentException("Payload too long");
+ }
+ int headerLength = getHeaderSize();
+ byte[] newBytes = new byte[headerLength + TAG_LENGTH + payload.length];
+ // Copy header (including the 4 bytes of tag integer at the beginning of payload)
+ System.arraycopy(mBuffer.array(), 0, newBytes, 0, headerLength + TAG_LENGTH);
+ // Fill in encoded objects
+ System.arraycopy(payload, 0, newBytes, headerLength + TAG_LENGTH, payload.length);
+ Event result = new Event(newBytes);
+ // Patch payload length in header
+ result.mBuffer.putShort(LENGTH_OFFSET, (short) (payload.length + TAG_LENGTH));
+ return result;
+ }
+
/** @return the loggable item at the current position in mBuffer. */
private Object decodeObject() {
byte type = mBuffer.get();
@@ -190,6 +214,66 @@
}
}
+ private static @NonNull byte[] encodeObject(@Nullable Object object) {
+ if (object == null) {
+ return new byte[0];
+ }
+ if (object instanceof Integer) {
+ return ByteBuffer.allocate(1 + 4)
+ .order(ByteOrder.nativeOrder())
+ .put(INT_TYPE)
+ .putInt((Integer) object)
+ .array();
+ } else if (object instanceof Long) {
+ return ByteBuffer.allocate(1 + 8)
+ .order(ByteOrder.nativeOrder())
+ .put(LONG_TYPE)
+ .putLong((Long) object)
+ .array();
+ } else if (object instanceof Float) {
+ return ByteBuffer.allocate(1 + 4)
+ .order(ByteOrder.nativeOrder())
+ .put(FLOAT_TYPE)
+ .putFloat((Float) object)
+ .array();
+ } else if (object instanceof String) {
+ String string = (String) object;
+ byte[] bytes;
+ try {
+ bytes = string.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ bytes = new byte[0];
+ }
+ return ByteBuffer.allocate(1 + 4 + bytes.length)
+ .order(ByteOrder.nativeOrder())
+ .put(STRING_TYPE)
+ .putInt(bytes.length)
+ .put(bytes)
+ .array();
+ } else if (object instanceof Object[]) {
+ Object[] objects = (Object[]) object;
+ if (objects.length > 255) {
+ throw new IllegalArgumentException("Object array too long");
+ }
+ byte[][] bytes = new byte[objects.length][];
+ int totalLength = 0;
+ for (int i = 0; i < objects.length; i++) {
+ bytes[i] = encodeObject(objects[i]);
+ totalLength += bytes[i].length;
+ }
+ ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + totalLength)
+ .order(ByteOrder.nativeOrder())
+ .put(LIST_TYPE)
+ .put((byte) objects.length);
+ for (int i = 0; i < objects.length; i++) {
+ buffer.put(bytes[i]);
+ }
+ return buffer.array();
+ } else {
+ throw new IllegalArgumentException("Unknown object type " + object);
+ }
+ }
+
/** @hide */
public static Event fromBytes(byte[] data) {
return new Event(data);
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index 73e17a6..55542d8 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -16,15 +16,18 @@
package android.util;
+import android.annotation.NonNull;
import android.os.Parcel;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.LongObjPredicate;
import libcore.util.EmptyArray;
import java.util.Arrays;
+import java.util.Objects;
/**
* SparseArray mapping longs to Objects. Unlike a normal array of Objects,
@@ -145,6 +148,18 @@
delete(key);
}
+ /** @hide */
+ @SuppressWarnings("unchecked")
+ public void removeIf(@NonNull LongObjPredicate<? super E> filter) {
+ Objects.requireNonNull(filter);
+ for (int i = 0; i < mSize; ++i) {
+ if (mValues[i] != DELETED && filter.test(mKeys[i], (E) mValues[i])) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ }
+ }
+ }
+
/**
* Removes the mapping at the specified index.
*
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index deff79d5..73707ca 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1586,6 +1586,19 @@
info.addChild(wrapper.getLeashToken());
}
+ @Override
+ public int getImportantForAccessibility() {
+ final int mode = super.getImportantForAccessibility();
+ // If developers explicitly set the important mode for it, don't change the mode.
+ // Only change the mode to important when this SurfaceView isn't explicitly set and has
+ // an embedded hierarchy.
+ if (mRemoteAccessibilityEmbeddedConnection == null
+ || mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ return mode;
+ }
+ return IMPORTANT_FOR_ACCESSIBILITY_YES;
+ }
+
private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
final RemoteAccessibilityEmbeddedConnection wrapper =
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4c7307e..11ab572 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13442,8 +13442,7 @@
* @see #getImportantForAccessibility()
*/
public boolean isImportantForAccessibility() {
- final int mode = (mPrivateFlags2 & PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK)
- >> PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT;
+ final int mode = getImportantForAccessibility();
if (mode == IMPORTANT_FOR_ACCESSIBILITY_NO
|| mode == IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
return false;
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index cf34b0b..f406be9 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -26,6 +26,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.view.SurfaceControl;
import java.util.ArrayList;
import java.util.List;
@@ -77,8 +78,28 @@
public WindowContainerTransaction scheduleFinishEnterPip(IWindowContainer container,
Rect bounds) {
Change chg = getOrCreateChange(container.asBinder());
- chg.mSchedulePipCallback = true;
chg.mPinnedBounds = new Rect(bounds);
+ chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
+
+ return this;
+ }
+
+ /**
+ * Send a SurfaceControl transaction to the server, which the server will apply in sync with
+ * the next bounds change. As this uses deferred transaction and not BLAST it is only
+ * able to sync with a single window, and the first visible window in this hierarchy of type
+ * BASE_APPLICATION to resize will be used. If there are bound changes included in this
+ * WindowContainer transaction (from setBounds or scheduleFinishEnterPip), the SurfaceControl
+ * transaction will be synced with those bounds. If there are no changes, then
+ * the SurfaceControl transaction will be synced with the next bounds change. This means
+ * that you can call this, apply the WindowContainer transaction, and then later call
+ * dismissPip() to achieve synchronization.
+ */
+ public WindowContainerTransaction setBoundsChangeTransaction(IWindowContainer container,
+ SurfaceControl.Transaction t) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mBoundsChangeTransaction = t;
+ chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION;
return this;
}
@@ -174,6 +195,8 @@
*/
public static class Change implements Parcelable {
public static final int CHANGE_FOCUSABLE = 1;
+ public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
+ public static final int CHANGE_PIP_CALLBACK = 1 << 2;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
@@ -181,8 +204,8 @@
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
- private boolean mSchedulePipCallback = false;
private Rect mPinnedBounds = null;
+ private SurfaceControl.Transaction mBoundsChangeTransaction = null;
public Change() {}
@@ -192,11 +215,14 @@
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
- mSchedulePipCallback = (in.readInt() != 0);
- if (mSchedulePipCallback ) {
+ if ((mChangeMask & Change.CHANGE_PIP_CALLBACK) != 0) {
mPinnedBounds = new Rect();
mPinnedBounds.readFromParcel(in);
}
+ if ((mChangeMask & Change.CHANGE_BOUNDS_TRANSACTION) != 0) {
+ mBoundsChangeTransaction =
+ SurfaceControl.Transaction.CREATOR.createFromParcel(in);
+ }
}
public Configuration getConfiguration() {
@@ -233,6 +259,10 @@
return mPinnedBounds;
}
+ public SurfaceControl.Transaction getBoundsChangeTransaction() {
+ return mBoundsChangeTransaction;
+ }
+
@Override
public String toString() {
final boolean changesBounds =
@@ -264,10 +294,12 @@
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
- dest.writeInt(mSchedulePipCallback ? 1 : 0);
- if (mSchedulePipCallback ) {
+ if (mPinnedBounds != null) {
mPinnedBounds.writeToParcel(dest, flags);
}
+ if (mBoundsChangeTransaction != null) {
+ mBoundsChangeTransaction.writeToParcel(dest, flags);
+ }
}
@Override
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 56683dd..d40f505 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -101,7 +101,7 @@
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
- mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
+ mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow);
mIsViewAdded = true;
mLastView = view;
mLastParams = (WindowManager.LayoutParams) params;
@@ -158,7 +158,7 @@
@Override
public Display getDefaultDisplay() {
- return mContext.getDisplay();
+ return mContext.getDisplayNoVerify();
}
@Override
@@ -240,7 +240,7 @@
private Rect getMaximumBounds() {
// TODO(b/128338354): Current maximum bound is display size, but it should be displayArea
// bound after displayArea feature is finished.
- final Display display = mContext.getDisplay();
+ final Display display = mContext.getDisplayNoVerify();
final Point displaySize = new Point();
display.getRealSize(displaySize);
return new Rect(0, 0, displaySize.x, displaySize.y);
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
index 265674a..c6f42f7 100644
--- a/core/java/android/view/accessibility/OWNERS
+++ b/core/java/android/view/accessibility/OWNERS
@@ -1,3 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
+qasid@google.com
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index 8bda339..3cc04b8 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.Parcelable;
import android.util.Size;
@@ -40,15 +41,13 @@
private final Size mMaxSize;
/**
- * The fully qualified resource name of the UI style resource identifier, defaults to {@code
- * null}.
- *
- * <p> The value can be obtained by calling {@code Resources#getResourceName(int)}.
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
*/
@Nullable
- private final String mStyle;
+ private final Bundle mStyle;
- private static String defaultStyle() {
+ private static Bundle defaultStyle() {
return null;
}
@@ -76,7 +75,7 @@
/* package-private */ InlinePresentationSpec(
@NonNull Size minSize,
@NonNull Size maxSize,
- @Nullable String style) {
+ @Nullable Bundle style) {
this.mMinSize = minSize;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mMinSize);
@@ -105,13 +104,11 @@
}
/**
- * The fully qualified resource name of the UI style resource identifier, defaults to {@code
- * null}.
- *
- * <p> The value can be obtained by calling {@code Resources#getResourceName(int)}.
+ * The extras encoding the UI style information. Defaults to null in which case the default
+ * system UI style will be used.
*/
@DataClass.Generated.Member
- public @Nullable String getStyle() {
+ public @Nullable Bundle getStyle() {
return mStyle;
}
@@ -170,7 +167,7 @@
dest.writeByte(flg);
dest.writeSize(mMinSize);
dest.writeSize(mMaxSize);
- if (mStyle != null) dest.writeString(mStyle);
+ if (mStyle != null) dest.writeBundle(mStyle);
}
@Override
@@ -187,7 +184,7 @@
byte flg = in.readByte();
Size minSize = (Size) in.readSize();
Size maxSize = (Size) in.readSize();
- String style = (flg & 0x4) == 0 ? null : in.readString();
+ Bundle style = (flg & 0x4) == 0 ? null : in.readBundle();
this.mMinSize = minSize;
com.android.internal.util.AnnotationValidations.validate(
@@ -223,7 +220,7 @@
private @NonNull Size mMinSize;
private @NonNull Size mMaxSize;
- private @Nullable String mStyle;
+ private @Nullable Bundle mStyle;
private long mBuilderFieldsSet = 0L;
@@ -247,13 +244,11 @@
}
/**
- * The fully qualified resource name of the UI style resource identifier, defaults to {@code
- * null}.
- *
- * <p> The value can be obtained by calling {@code Resources#getResourceName(int)}.
+ * The extras encoding the UI style information. Defaults to null in which case the default
+ * system UI style will be used.
*/
@DataClass.Generated.Member
- public @NonNull Builder setStyle(@Nullable String value) {
+ public @NonNull Builder setStyle(@Nullable Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mStyle = value;
@@ -284,10 +279,10 @@
}
@DataClass.Generated(
- time = 1581736227796L,
+ time = 1582078731418L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
- inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static java.lang.String defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 91e15c1..71c9e33 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -21,17 +21,16 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Log;
import android.view.View;
-import android.view.autofill.AutofillId;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
/**
* The InputMethod interface represents an input method which can generate key
@@ -114,14 +113,13 @@
/**
* Called to notify the IME that Autofill Frameworks requested an inline suggestions request.
*
- * @param componentName {@link ComponentName} of current app/activity.
- * @param autofillId {@link AutofillId} of currently focused field.
+ * @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
* @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
*
* @hide
*/
- default void onCreateInlineSuggestionsRequest(ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
+ default void onCreateInlineSuggestionsRequest(InlineSuggestionsRequestInfo requestInfo,
+ IInlineSuggestionsRequestCallback cb) {
try {
cb.onInlineSuggestionsUnsupported();
} catch (RemoteException e) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index dbab81b1..39d5f5c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -93,12 +93,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
@@ -415,16 +410,6 @@
int mCursorCandEnd;
/**
- * Initial startInput with {@link StartInputReason.WINDOW_FOCUS_GAIN} is executed
- * in a background thread. Later, if there is an actual startInput it will wait on
- * main thread till the background thread completes.
- */
- private Future<?> mWindowFocusGainFuture;
-
- private ExecutorService mStartInputWorker = Executors.newSingleThreadExecutor(
- new ImeThreadFactory("StartInputWorker"));
-
- /**
* The instance that has previously been sent to the input method.
*/
private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -612,41 +597,36 @@
final boolean forceNewFocus1 = forceNewFocus;
final int startInputFlags = getStartInputFlags(focusedView, 0);
- if (mWindowFocusGainFuture != null) {
- mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
+ final ImeFocusController controller = getFocusController();
+ if (controller == null) {
+ return;
}
- mWindowFocusGainFuture = mStartInputWorker.submit(() -> {
- final ImeFocusController controller = getFocusController();
- if (controller == null) {
+ if (controller.checkFocus(forceNewFocus1, false)) {
+ // We need to restart input on the current focus view. This
+ // should be done in conjunction with telling the system service
+ // about the window gaining focus, to help make the transition
+ // smooth.
+ if (startInput(StartInputReason.WINDOW_FOCUS_GAIN,
+ focusedView, startInputFlags, softInputMode, windowFlags)) {
return;
}
- if (controller.checkFocus(forceNewFocus1, false)) {
- // We need to restart input on the current focus view. This
- // should be done in conjunction with telling the system service
- // about the window gaining focus, to help make the transition
- // smooth.
- if (startInput(StartInputReason.WINDOW_FOCUS_GAIN,
- focusedView, startInputFlags, softInputMode, windowFlags)) {
- return;
- }
- }
+ }
- synchronized (mH) {
- // For some reason we didn't do a startInput + windowFocusGain, so
- // we'll just do a window focus gain and call it a day.
- try {
- if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
- mService.startInputOrWindowGainedFocus(
- StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
- focusedView.getWindowToken(), startInputFlags, softInputMode,
- windowFlags,
- null, null, 0 /* missingMethodFlags */,
- mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ synchronized (mH) {
+ // For some reason we didn't do a startInput + windowFocusGain, so
+ // we'll just do a window focus gain and call it a day.
+ try {
+ if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
+ mService.startInputOrWindowGainedFocus(
+ StartInputReason.WINDOW_FOCUS_GAIN_REPORT_ONLY, mClient,
+ focusedView.getWindowToken(), startInputFlags, softInputMode,
+ windowFlags,
+ null, null, 0 /* missingMethodFlags */,
+ mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- });
+ }
}
/**
@@ -664,10 +644,6 @@
*/
@Override
public void setCurrentRootView(ViewRootImpl rootView) {
- if (mWindowFocusGainFuture != null) {
- mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
- mWindowFocusGainFuture = null;
- }
synchronized (mH) {
if (mCurRootView != null) {
// Reset the last served view and restart window focus state of the root view.
@@ -845,19 +821,16 @@
} catch (RemoteException e) {
}
}
- }
- // Check focus again in case that "onWindowFocus" is called before
- // handling this message.
- final View servedView;
- synchronized (mH) {
- servedView = getServedViewLocked();
- }
- if (servedView != null && canStartInput(servedView)) {
- if (mCurRootView != null && mCurRootView.getImeFocusController()
- .checkFocus(mRestartOnNextWindowFocus, false)) {
- final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
- : StartInputReason.DEACTIVATED_BY_IMMS;
- mDelegate.startInput(reason, null, 0, 0, 0);
+ // Check focus again in case that "onWindowFocus" is called before
+ // handling this message.
+ final View servedView = getServedViewLocked();
+ if (servedView != null && canStartInput(servedView)) {
+ if (mCurRootView != null && mCurRootView.getImeFocusController()
+ .checkFocus(mRestartOnNextWindowFocus, false)) {
+ final int reason = active ? StartInputReason.ACTIVATED_BY_IMMS
+ : StartInputReason.DEACTIVATED_BY_IMMS;
+ mDelegate.startInput(reason, null, 0, 0, 0);
+ }
}
}
return;
@@ -1035,6 +1008,13 @@
}
@Override
+ public void scheduleStartInputIfNecessary(boolean fullscreen) {
+ // TODO(b/149859205): See if we can optimize this by having a fused dedicated operation.
+ mH.obtainMessage(MSG_SET_ACTIVE, 0 /* active */, fullscreen ? 1 : 0).sendToTarget();
+ mH.obtainMessage(MSG_SET_ACTIVE, 1 /* active */, fullscreen ? 1 : 0).sendToTarget();
+ }
+
+ @Override
public void reportFullscreenMode(boolean fullscreen) {
mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, fullscreen ? 1 : 0, 0)
.sendToTarget();
@@ -1430,10 +1410,6 @@
*/
void clearBindingLocked() {
if (DEBUG) Log.v(TAG, "Clearing binding!");
- if (mWindowFocusGainFuture != null) {
- mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
- mWindowFocusGainFuture = null;
- }
clearConnectionLocked();
setInputChannelLocked(null);
mBindSequence = -1;
@@ -1826,18 +1802,6 @@
boolean startInputInner(@StartInputReason int startInputReason,
@Nullable IBinder windowGainingFocus, @StartInputFlags int startInputFlags,
@SoftInputModeFlags int softInputMode, int windowFlags) {
- if (startInputReason != StartInputReason.WINDOW_FOCUS_GAIN
- && mWindowFocusGainFuture != null) {
- try {
- mWindowFocusGainFuture.get();
- } catch (ExecutionException | InterruptedException e) {
- // do nothing
- } catch (CancellationException e) {
- // window no longer has focus.
- return true;
- }
- }
-
final View view;
synchronized (mH) {
view = getServedViewLocked();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 9de1222..13c1f67 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -83,6 +83,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
+import android.util.TypedValue;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.ContextMenu;
@@ -151,9 +152,6 @@
private static final String TAG = "Editor";
private static final boolean DEBUG_UNDO = false;
- // Specifies whether to allow starting a cursor drag by dragging anywhere over the text.
- @VisibleForTesting
- public static boolean FLAG_ENABLE_CURSOR_DRAG = true;
// Specifies whether to use the magnifier when pressing the insertion or selection handles.
private static final boolean FLAG_USE_MAGNIFIER = true;
@@ -387,13 +385,25 @@
private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
- // Specifies whether the cursor control feature set is enabled.
- // This can only be true if the text view is editable.
- private boolean mCursorControlEnabled;
+ private boolean mFlagCursorDragFromAnywhereEnabled;
+ private boolean mFlagInsertionHandleGesturesEnabled;
// Specifies whether the new magnifier (with fish-eye effect) is enabled.
private final boolean mNewMagnifierEnabled;
+ // Line height range in DP for the new magnifier.
+ static private final int MIN_LINE_HEIGHT_FOR_MAGNIFIER = 20;
+ static private final int MAX_LINE_HEIGHT_FOR_MAGNIFIER = 32;
+ // Line height range in pixels for the new magnifier.
+ // - If the line height is bigger than the max, magnifier should be dismissed.
+ // - If the line height is smaller than the min, magnifier should apply a bigger zoom factor
+ // to make sure the text can be seen clearly.
+ private int mMinLineHeightForMagnifier;
+ private int mMaxLineHeightForMagnifier;
+ // The zoom factor initially configured.
+ // The actual zoom value may changes based on this initial zoom value.
+ private float mInitialZoom = 1f;
+
Editor(TextView textView) {
mTextView = textView;
// Synchronize the filter list, which places the undo input filter at the end.
@@ -402,26 +412,43 @@
mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enableHapticTextHandle);
- mCursorControlEnabled = AppGlobals.getIntCoreSetting(
- WidgetFlags.KEY_ENABLE_CURSOR_CONTROL , 0) != 0;
+ mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
+ WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0;
+ mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting(
+ WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES,
+ WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0;
mNewMagnifierEnabled = AppGlobals.getIntCoreSetting(
- WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, 0) != 0;
+ WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER,
+ WidgetFlags.ENABLE_NEW_MAGNIFIER_DEFAULT ? 1 : 0) != 0;
if (TextView.DEBUG_CURSOR) {
- logCursor("Editor", "Cursor control is %s.",
- mCursorControlEnabled ? "enabled" : "disabled");
+ logCursor("Editor", "Cursor drag from anywhere is %s.",
+ mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled");
+ logCursor("Editor", "Insertion handle gestures is %s.",
+ mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled");
logCursor("Editor", "New magnifier is %s.",
mNewMagnifierEnabled ? "enabled" : "disabled");
}
}
@VisibleForTesting
- public void setCursorControlEnabled(boolean enabled) {
- mCursorControlEnabled = enabled;
+ public boolean getFlagCursorDragFromAnywhereEnabled() {
+ return mFlagCursorDragFromAnywhereEnabled;
}
@VisibleForTesting
- public boolean getCursorControlEnabled() {
- return mCursorControlEnabled;
+ public void setFlagCursorDragFromAnywhereEnabled(boolean enabled) {
+ mFlagCursorDragFromAnywhereEnabled = enabled;
+ }
+
+ @VisibleForTesting
+ public boolean getFlagInsertionHandleGesturesEnabled() {
+ return mFlagInsertionHandleGesturesEnabled;
+ }
+
+ @VisibleForTesting
+ public void setFlagInsertionHandleGesturesEnabled(boolean enabled) {
+ mFlagInsertionHandleGesturesEnabled = enabled;
}
// Lazy creates the magnifier animator.
@@ -440,12 +467,12 @@
private Magnifier.Builder createBuilderWithInlineMagnifierDefaults() {
final Magnifier.Builder params = new Magnifier.Builder(mTextView);
- // TODO: supports changing the height/width dynamically because the text height can be
- // dynamically changed.
float zoom = AppGlobals.getFloatCoreSetting(
- WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, 1.5f);
+ WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR,
+ WidgetFlags.MAGNIFIER_ZOOM_FACTOR_DEFAULT);
float aspectRatio = AppGlobals.getFloatCoreSetting(
- WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, 5.5f);
+ WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO,
+ WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT);
// Avoid invalid/unsupported values.
if (zoom < 1.2f || zoom > 1.8f) {
zoom = 1.5f;
@@ -454,13 +481,20 @@
aspectRatio = 5.5f;
}
+ mInitialZoom = zoom;
+ mMinLineHeightForMagnifier = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, MIN_LINE_HEIGHT_FOR_MAGNIFIER,
+ mTextView.getContext().getResources().getDisplayMetrics());
+ mMaxLineHeightForMagnifier = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, MAX_LINE_HEIGHT_FOR_MAGNIFIER,
+ mTextView.getContext().getResources().getDisplayMetrics());
+
final Layout layout = mTextView.getLayout();
final int line = layout.getLineForOffset(mTextView.getSelectionStart());
final int sourceHeight =
layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
- // Slightly increase the height to avoid tooLargeTextForMagnifier() returns true.
- int height = (int)(sourceHeight * zoom) + 2;
- int width = (int)(aspectRatio * height);
+ final int height = (int)(sourceHeight * zoom);
+ final int width = (int)(aspectRatio * Math.max(sourceHeight, mMinLineHeightForMagnifier));
params.setFishEyeStyle()
.setSize(width, height)
@@ -4902,6 +4936,12 @@
}
private boolean tooLargeTextForMagnifier() {
+ if (mNewMagnifierEnabled) {
+ Layout layout = mTextView.getLayout();
+ final int line = layout.getLineForOffset(getCurrentCursorOffset());
+ return layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line)
+ >= mMaxLineHeightForMagnifier;
+ }
final float magnifierContentHeight = Math.round(
mMagnifierAnimator.mMagnifier.getHeight()
/ mMagnifierAnimator.mMagnifier.getZoom());
@@ -5111,9 +5151,16 @@
int lineRight = (int) layout.getLineRight(line);
lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight);
+ final int lineHeight =
+ layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
+ float zoom = mInitialZoom;
+ if (lineHeight < mMinLineHeightForMagnifier) {
+ zoom = zoom * mMinLineHeightForMagnifier / lineHeight;
+ }
+ mMagnifierAnimator.mMagnifier.updateSourceFactors(lineHeight, zoom);
mMagnifierAnimator.mMagnifier.show(showPosInView.x, showPosInView.y);
} else {
- mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
+ mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
}
updateHandlesVisibility();
} else {
@@ -5259,11 +5306,13 @@
int deltaHeight = 0;
int opacity = 255;
- if (mCursorControlEnabled) {
+ if (mFlagInsertionHandleGesturesEnabled) {
deltaHeight = AppGlobals.getIntCoreSetting(
- WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, 25);
+ WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT,
+ WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT_DEFAULT);
opacity = AppGlobals.getIntCoreSetting(
- WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, 50);
+ WidgetFlags.KEY_INSERTION_HANDLE_OPACITY,
+ WidgetFlags.INSERTION_HANDLE_OPACITY_DEFAULT);
// Avoid invalid/unsupported values.
if (deltaHeight < -25 || deltaHeight > 50) {
deltaHeight = 25;
@@ -5329,7 +5378,7 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mCursorControlEnabled) {
+ if (mFlagInsertionHandleGesturesEnabled) {
final int height = Math.max(
getPreferredHeight() + mDeltaHeight, mDrawable.getIntrinsicHeight());
setMeasuredDimension(getPreferredWidth(), height);
@@ -5340,7 +5389,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mCursorControlEnabled && FLAG_ENABLE_CURSOR_DRAG) {
+ if (mFlagInsertionHandleGesturesEnabled && mFlagCursorDragFromAnywhereEnabled) {
// Should only enable touch through when cursor drag is enabled.
// Otherwise the insertion handle view cannot be moved.
return touchThrough(ev);
@@ -6031,7 +6080,7 @@
}
if (mIsDraggingCursor) {
performCursorDrag(event);
- } else if (FLAG_ENABLE_CURSOR_DRAG
+ } else if (mFlagCursorDragFromAnywhereEnabled
&& mTextView.getLayout() != null
&& mTextView.isFocused()
&& mTouchState.isMovedEnoughForDrag()
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 47ea1cb..a299b01 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -89,7 +89,7 @@
// The width of the window containing the magnifier.
private final int mWindowWidth;
// The height of the window containing the magnifier.
- private final int mWindowHeight;
+ private int mWindowHeight;
// The zoom applied to the view region copied to the magnifier view.
private float mZoom;
// The width of the content that will be copied to the magnifier.
@@ -485,6 +485,21 @@
}
/**
+ * Updates the factors of source which may impact the magnifier's size.
+ * This can be called while the magnifier is showing and moving.
+ * @param sourceHeight the new source height.
+ * @param zoom the new zoom factor.
+ */
+ void updateSourceFactors(final int sourceHeight, final float zoom) {
+ mZoom = zoom;
+ mSourceHeight = sourceHeight;
+ mWindowHeight = (int) (sourceHeight * zoom);
+ if (mWindow != null) {
+ mWindow.updateContentFactors(mWindowHeight, zoom);
+ }
+ }
+
+ /**
* Returns the zoom to be applied to the magnified view region copied to the magnifier.
* If the zoom is x and the magnifier window size is (width, height), the original size
* of the content being magnified will be (width / x, height / x).
@@ -904,7 +919,7 @@
private final Display mDisplay;
// The size of the content of the magnifier.
private final int mContentWidth;
- private final int mContentHeight;
+ private int mContentHeight;
// The insets of the content inside the allocated surface.
private final int mOffsetX;
private final int mOffsetY;
@@ -947,7 +962,7 @@
// The current content of the magnifier. It is mBitmap + mOverlay, only used for testing.
private Bitmap mCurrentContent;
- private final float mZoom;
+ private float mZoom;
// The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
private final int mRamp;
// Whether is in the new magnifier style.
@@ -1009,11 +1024,11 @@
final RecordingCanvas canvas = mRenderer.getRootNode().beginRecording(width, height);
try {
- canvas.insertReorderBarrier();
+ canvas.enableZ();
canvas.drawRenderNode(mBitmapRenderNode);
- canvas.insertInorderBarrier();
+ canvas.disableZ();
canvas.drawRenderNode(mOverlayRenderNode);
- canvas.insertInorderBarrier();
+ canvas.disableZ();
} finally {
mRenderer.getRootNode().endRecording();
}
@@ -1034,15 +1049,66 @@
}
}
+ /**
+ * Updates the factors of content which may resize the window.
+ * @param contentHeight the new height of content.
+ * @param zoom the new zoom factor.
+ */
+ private void updateContentFactors(final int contentHeight, final float zoom) {
+ if (mContentHeight == contentHeight && mZoom == zoom) {
+ return;
+ }
+ if (mContentHeight < contentHeight) {
+ // Grows the surface height as necessary.
+ new SurfaceControl.Transaction().setBufferSize(
+ mSurfaceControl, mContentWidth, contentHeight).apply();
+ mSurface.copyFrom(mSurfaceControl);
+ mRenderer.setSurface(mSurface);
+
+ final Outline outline = new Outline();
+ outline.setRoundRect(0, 0, mContentWidth, contentHeight, 0);
+ outline.setAlpha(1.0f);
+
+ mBitmapRenderNode.setLeftTopRightBottom(mOffsetX, mOffsetY,
+ mOffsetX + mContentWidth, mOffsetY + contentHeight);
+ mBitmapRenderNode.setOutline(outline);
+
+ mOverlayRenderNode.setLeftTopRightBottom(mOffsetX, mOffsetY,
+ mOffsetX + mContentWidth, mOffsetY + contentHeight);
+ mOverlayRenderNode.setOutline(outline);
+
+ final RecordingCanvas canvas =
+ mRenderer.getRootNode().beginRecording(mContentWidth, contentHeight);
+ try {
+ canvas.enableZ();
+ canvas.drawRenderNode(mBitmapRenderNode);
+ canvas.disableZ();
+ canvas.drawRenderNode(mOverlayRenderNode);
+ canvas.disableZ();
+ } finally {
+ mRenderer.getRootNode().endRecording();
+ }
+ }
+ mContentHeight = contentHeight;
+ mZoom = zoom;
+ fillMeshMatrix();
+ }
+
private void createMeshMatrixForFishEyeEffect() {
mMeshWidth = 1;
mMeshHeight = 6;
+ mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
+ mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
+ fillMeshMatrix();
+ }
+
+ private void fillMeshMatrix() {
+ mMeshWidth = 1;
+ mMeshHeight = 6;
final float w = mContentWidth;
final float h = mContentHeight;
final float h0 = h / mZoom;
final float dh = h - h0;
- mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
- mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) {
// Calculates X value.
final int colIndex = i % (2 * (mMeshWidth + 1)) / 2;
@@ -1197,6 +1263,7 @@
if (mBitmap != null) {
mBitmap.recycle();
}
+ mOverlay.setCallback(null);
}
private void doDraw() {
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index 1a8e7a7..bce5497 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -17,27 +17,49 @@
package android.widget;
/**
- * Keeps the flags related to the Widget namespace in {@link DeviceConfig}.
+ * Flags in the {@link android.provider.DeviceConfig#NAMESPACE_WIDGET "widget" namespace}.
*
* @hide
*/
public final class WidgetFlags {
/**
- * Whether the cursor control feature set is enabled.
- * TODO: Makes this flag key visible to webview/chrome.
+ * Whether starting a cursor drag from anywhere in the text should be enabled.
*/
- public static final String ENABLE_CURSOR_CONTROL =
- "CursorControlFeature__enable_cursor_control";
+ public static final String ENABLE_CURSOR_DRAG_FROM_ANYWHERE =
+ "CursorControlFeature__enable_cursor_drag_from_anywhere";
/**
- * The key name used in app core settings for enable cursor control.
+ * The key used in app core settings for the flag {@link #ENABLE_CURSOR_DRAG_FROM_ANYWHERE}.
*/
- public static final String KEY_ENABLE_CURSOR_CONTROL = "widget__enable_cursor_control";
+ public static final String KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE =
+ "widget__enable_cursor_drag_from_anywhere";
+
+ /**
+ * Default value for the flag {@link #ENABLE_CURSOR_DRAG_FROM_ANYWHERE}.
+ */
+ public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
+
+ /**
+ * Whether additional gestures should be enabled for the insertion cursor handle (e.g.
+ * long-press or double-tap on the handle to trigger selection).
+ */
+ public static final String ENABLE_INSERTION_HANDLE_GESTURES =
+ "CursorControlFeature__enable_insertion_handle_gestures";
+
+ /**
+ * The key used in app core settings for the flag {@link #ENABLE_INSERTION_HANDLE_GESTURES}.
+ */
+ public static final String KEY_ENABLE_INSERTION_HANDLE_GESTURES =
+ "widget__enable_insertion_handle_gestures";
+
+ /**
+ * Default value for the flag {@link #ENABLE_INSERTION_HANDLE_GESTURES}.
+ */
+ public static final boolean ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT = false;
/**
* The flag of delta height applies to the insertion handle when cursor control flag is enabled.
- * The default value is 25.
*/
public static final String INSERTION_HANDLE_DELTA_HEIGHT =
"CursorControlFeature__insertion_handle_delta_height";
@@ -49,8 +71,13 @@
"widget__insertion_handle_delta_height";
/**
+ * Default value for the flag {@link #INSERTION_HANDLE_DELTA_HEIGHT}.
+ */
+ public static final int INSERTION_HANDLE_DELTA_HEIGHT_DEFAULT = 25;
+
+ /**
* The flag of opacity applies to the insertion handle when cursor control flag is enabled.
- * The opacity value is in the range of {0..100}. The default value is 50.
+ * The opacity value is in the range of {0..100}.
*/
public static final String INSERTION_HANDLE_OPACITY =
"CursorControlFeature__insertion_handle_opacity";
@@ -62,6 +89,11 @@
"widget__insertion_handle_opacity";
/**
+ * Default value for the flag {@link #INSERTION_HANDLE_OPACITY}.
+ */
+ public static final int INSERTION_HANDLE_OPACITY_DEFAULT = 50;
+
+ /**
* The flag of enabling the new magnifier.
*/
public static final String ENABLE_NEW_MAGNIFIER = "CursorControlFeature__enable_new_magnifier";
@@ -72,8 +104,12 @@
public static final String KEY_ENABLE_NEW_MAGNIFIER = "widget__enable_new_magnifier";
/**
+ * Default value for the flag {@link #ENABLE_NEW_MAGNIFIER}.
+ */
+ public static final boolean ENABLE_NEW_MAGNIFIER_DEFAULT = false;
+
+ /**
* The flag of zoom factor applies to the new magnifier.
- * The default value is 1.5f.
*/
public static final String MAGNIFIER_ZOOM_FACTOR =
"CursorControlFeature__magnifier_zoom_factor";
@@ -84,8 +120,12 @@
public static final String KEY_MAGNIFIER_ZOOM_FACTOR = "widget__magnifier_zoom_factor";
/**
+ * Default value for the flag {@link #MAGNIFIER_ZOOM_FACTOR}.
+ */
+ public static final float MAGNIFIER_ZOOM_FACTOR_DEFAULT = 1.5f;
+
+ /**
* The flag of aspect ratio (width/height) applies to the new magnifier.
- * The default value is 5.5f.
*/
public static final String MAGNIFIER_ASPECT_RATIO =
"CursorControlFeature__magnifier_aspect_ratio";
@@ -95,6 +135,11 @@
*/
public static final String KEY_MAGNIFIER_ASPECT_RATIO = "widget__magnifier_aspect_ratio";
+ /**
+ * Default value for the flag {@link #MAGNIFIER_ASPECT_RATIO}.
+ */
+ public static final float MAGNIFIER_ASPECT_RATIO_DEFAULT = 5.5f;
+
private WidgetFlags() {
}
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a201a33..250b1ea 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1805,6 +1805,10 @@
if (!TextUtils.isEmpty(dataString)) {
return new IntentFilter(intent.getAction(), dataString);
}
+ if (intent.getType() == null) {
+ Log.e(TAG, "Failed to get target intent filter: intent data and type are null");
+ return null;
+ }
IntentFilter intentFilter = new IntentFilter(intent.getAction(), intent.getType());
List<Uri> contentUris = new ArrayList<>();
if (Intent.ACTION_SEND.equals(intent.getAction())) {
@@ -1825,7 +1829,7 @@
}
return intentFilter;
} catch (Exception e) {
- Log.e(TAG, "failed to get target intent filter", e);
+ Log.e(TAG, "Failed to get target intent filter", e);
return null;
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 2f1a15f..dc6942c 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -133,7 +133,7 @@
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
- int chargeUAh, int chargeFullUAh);
+ int chargeUAh, int chargeFullUAh, long chargeTimeToFullSeconds);
@UnsupportedAppUsage
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
index 84fed9c..8239018 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -24,10 +24,10 @@
import com.android.internal.widget.ViewPager;
/**
- * A {@link ViewPager} which wraps around its first child's height and has swiping disabled.
+ * A {@link ViewPager} which wraps around its first child's height.
* <p>Normally {@link ViewPager} instances expand their height to cover all remaining space in
* the layout.
- * <p>This class is used for the intent resolver and share sheet's tabbed view.
+ * <p>This class is used for the intent resolver's tabbed view.
*/
public class ResolverViewPager extends ViewPager {
@@ -70,14 +70,4 @@
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 0ccc45e..04bf915 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.os.IBinder;
import android.os.SELinux;
@@ -95,20 +94,12 @@
}
}
- public static Handle create(AndroidPackage pkg) throws IOException {
- return create(
- pkg.makeListAllCodePaths(),
- (pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) != 0,
- (pkg.getFlags() & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
- (pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
- }
-
public static Handle create(PackageLite lite) throws IOException {
return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
lite.debuggable);
}
- private static Handle create(List<String> codePaths, boolean multiArch,
+ public static Handle create(List<String> codePaths, boolean multiArch,
boolean extractNativeLibs, boolean debuggable) throws IOException {
final int size = codePaths.size();
final String[] apkPaths = new String[size];
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index f699eb8..72f16e4 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackagePartitions;
-import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsingPackageRead;
import android.os.Build;
import android.os.Process;
import android.os.Trace;
@@ -36,7 +36,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
@@ -71,10 +71,10 @@
* Interface for providing information on scanned packages.
* TODO(147840005): Remove this when android:isStatic and android:priority are fully deprecated
*/
- public interface AndroidPackageProvider {
+ public interface PackageProvider {
/** Performs the given action for each package. */
- void forEachPackage(Consumer<AndroidPackage> p);
+ void forEachPackage(BiConsumer<ParsingPackageRead, Boolean> p);
}
private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
@@ -100,7 +100,7 @@
@VisibleForTesting
public OverlayConfig(@Nullable File rootDirectory,
@Nullable Supplier<OverlayScanner> scannerFactory,
- @Nullable AndroidPackageProvider packageProvider) {
+ @Nullable PackageProvider packageProvider) {
Preconditions.checkArgument((scannerFactory == null) != (packageProvider == null),
"scannerFactory and packageProvider cannot be both null or both non-null");
@@ -186,13 +186,6 @@
*/
@NonNull
public static OverlayConfig getZygoteInstance() {
- if (Process.myUid() != Process.ROOT_UID) {
- // Scan the overlays in the zygote process to generate configuration settings for
- // overlays on the system image. Do not cache this instance so OverlayConfig will not
- // be present in applications by default.
- throw new IllegalStateException("Can only be invoked in the root process");
- }
-
Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#getZygoteInstance");
try {
return new OverlayConfig(null /* rootDirectory */, OverlayScanner::new,
@@ -208,20 +201,19 @@
* {@link #getSystemInstance()} will return the initialized instance.
*/
@NonNull
- public static OverlayConfig initializeSystemInstance(AndroidPackageProvider packageProvider) {
- if (Process.myUid() != Process.SYSTEM_UID) {
- throw new IllegalStateException("Can only be invoked in the system process");
- }
-
+ public static OverlayConfig initializeSystemInstance(PackageProvider packageProvider) {
Trace.traceBegin(Trace.TRACE_TAG_RRO, "OverlayConfig#initializeSystemInstance");
- sInstance = new OverlayConfig(null, null, packageProvider);
- Trace.traceEnd(Trace.TRACE_TAG_RRO);
+ try {
+ sInstance = new OverlayConfig(null, null, packageProvider);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_RRO);
+ }
return sInstance;
}
/**
* Retrieves the singleton instance initialized by
- * {@link #initializeSystemInstance(AndroidPackageProvider)}.
+ * {@link #initializeSystemInstance(PackageProvider)}.
*/
@NonNull
public static OverlayConfig getSystemInstance() {
@@ -291,10 +283,10 @@
@NonNull
private static ArrayList<ParsedOverlayInfo> getOverlayPackageInfos(
- @NonNull AndroidPackageProvider packageManager) {
+ @NonNull PackageProvider packageManager) {
final ArrayList<ParsedOverlayInfo> overlays = new ArrayList<>();
- packageManager.forEachPackage((AndroidPackage p) -> {
- if (p.getOverlayTarget() != null && p.isSystem()) {
+ packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem) -> {
+ if (p.getOverlayTarget() != null && isSystem) {
overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
new File(p.getBaseCodePath())));
@@ -373,10 +365,6 @@
*/
@NonNull
public String[] createImmutableFrameworkIdmapsInZygote() {
- if (Process.myUid() != Process.ROOT_UID) {
- throw new IllegalStateException("This method can only be called from the root process");
- }
-
final String targetPath = "/system/framework/framework-res.apk";
final ArrayList<String> idmapPaths = new ArrayList<>();
final ArrayList<IdmapInvocation> idmapInvocations =
diff --git a/core/java/com/android/internal/content/om/TEST_MAPPING b/core/java/com/android/internal/content/om/TEST_MAPPING
new file mode 100644
index 0000000..4cb595b
--- /dev/null
+++ b/core/java/com/android/internal/content/om/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.content."
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index bbae027..23b1ab5 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Ikev2VpnProfile;
import android.net.ProxyInfo;
import android.os.Build;
import android.os.Parcel;
@@ -332,15 +333,38 @@
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
+ /** Checks if this profile specifies a LegacyVpn type. */
+ public static boolean isLegacyType(int type) {
+ switch (type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ private boolean isValidLockdownLegacyVpnProfile() {
+ return isLegacyType(type) && isServerAddressNumeric() && hasDns()
+ && areDnsAddressesNumeric();
+ }
+
+ private boolean isValidLockdownPlatformVpnProfile() {
+ return Ikev2VpnProfile.isValidVpnProfile(this);
+ }
+
/**
- * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS.
- * Server hostnames would require using DNS before connection.
+ * Tests if profile is valid for lockdown.
+ *
+ * <p>For LegacyVpn profiles, this requires an IPv4 address for both the server and DNS.
+ *
+ * <p>For PlatformVpn profiles, this requires a server, an identifier and the relevant fields to
+ * be non-null.
*/
public boolean isValidLockdownProfile() {
return isTypeValidForLockdown()
- && isServerAddressNumeric()
- && hasDns()
- && areDnsAddressesNumeric();
+ && (isValidLockdownLegacyVpnProfile() || isValidLockdownPlatformVpnProfile());
}
/** Returns {@code true} if the VPN type is valid for lockdown. */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 18066dc..27c7cb6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1000,6 +1000,8 @@
private int mMinLearnedBatteryCapacity = -1;
private int mMaxLearnedBatteryCapacity = -1;
+ private long mBatteryTimeToFullSeconds = -1;
+
private long[] mCpuFreqs;
@VisibleForTesting
@@ -12226,7 +12228,7 @@
@GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int volt, final int chargeUAh,
- final int chargeFullUAh) {
+ final int chargeFullUAh, final long chargeTimeToFullSeconds) {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
@@ -12429,6 +12431,8 @@
mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
}
mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
+
+ mBatteryTimeToFullSeconds = chargeTimeToFullSeconds;
}
public static boolean isOnBattery(int plugType, int status) {
@@ -12578,19 +12582,10 @@
// Not yet working.
return -1;
}
- /* Broken
- int curLevel = mCurrentBatteryLevel;
- int plugLevel = mDischargePlugLevel;
- if (plugLevel < 0 || curLevel < (plugLevel+1)) {
- return -1;
+ if (mBatteryTimeToFullSeconds >= 0) {
+ return mBatteryTimeToFullSeconds * (1000 * 1000); // s to us
}
- long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
- if (duration < 1000*1000) {
- return -1;
- }
- long usPerLevel = duration/(curLevel-plugLevel);
- return usPerLevel * (100-curLevel);
- */
+ // Else use algorithmic approach
if (mChargeStepTracker.mNumStepDurations < 1) {
return -1;
}
@@ -12598,7 +12593,7 @@
if (msPerLevel <= 0) {
return -1;
}
- return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
+ return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
}
/*@hide */
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 3db8f4e..add2304 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -393,6 +393,9 @@
}
public int getNumCoresInCpuCluster(int cluster) {
+ if (cluster < 0 || cluster >= mCpuClusters.length) {
+ return 0; // index out of bound
+ }
return mCpuClusters[cluster].numCpus;
}
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
new file mode 100644
index 0000000..f44b9fb
--- /dev/null
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -0,0 +1,30 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.os.KernelCpuUidFreqTimeReaderTest"
+ },
+ {
+ "include-filter": "com.android.internal.os.KernelCpuUidActiveTimeReaderTest"
+ },
+ {
+ "include-filter": "com.android.internal.os.KernelCpuUidClusterTimeReaderTest"
+ },
+ {
+ "include-filter": "com.android.internal.os.KernelSingleUidTimeReaderTest"
+ },
+ {
+ "include-filter": "com.android.internal.os.KernelCpuUidBpfMapReaderTest"
+ }
+
+ ],
+ "file_patterns": [
+ "KernelCpuUidTimeReader\\.java",
+ "KernelCpuUidBpfMapReader\\.java",
+ "KernelSingleUidTimeReader\\.java"
+ ]
+ }
+ ]
+}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 4dac542..9b2bcfb 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -186,6 +186,17 @@
}
/**
+ * Returns the given map, or an immutable empty map if the provided map is null
+ *
+ * This can be used to guarantee null-safety without paying the price of extra allocations
+ *
+ * @see Collections#emptyMap
+ */
+ public static @NonNull <K, V> Map<K, V> emptyIfNull(@Nullable Map<K, V> cur) {
+ return cur == null ? Collections.emptyMap() : cur;
+ }
+
+ /**
* Returns the size of the given collection, or 0 if null
*/
public static int size(@Nullable Collection<?> cur) {
@@ -297,6 +308,33 @@
}
/**
+ * Similar to {@link List#add(int, Object)}, but with support for list values of {@code null}
+ * and {@link Collections#emptyList}
+ */
+ public static @NonNull <T> List<T> add(@Nullable List<T> cur, int index, T val) {
+ if (cur == null || cur == Collections.emptyList()) {
+ cur = new ArrayList<>();
+ }
+ cur.add(index, val);
+ return cur;
+ }
+
+ /**
+ * Similar to {@link Set#addAll(Collection)}}, but with support for list values of {@code null}
+ * and {@link Collections#emptySet}
+ */
+ public static @NonNull <T> Set<T> addAll(@Nullable Set<T> cur, @Nullable Collection<T> val) {
+ if (isEmpty(val)) {
+ return cur != null ? cur : Collections.emptySet();
+ }
+ if (cur == null || cur == Collections.emptySet()) {
+ cur = new ArraySet<>();
+ }
+ cur.addAll(val);
+ return cur;
+ }
+
+ /**
* @see #add(List, Object)
*/
public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java
index 390c596..6258a69 100644
--- a/core/java/com/android/internal/util/Parcelling.java
+++ b/core/java/com/android/internal/util/Parcelling.java
@@ -15,10 +15,18 @@
*/
package com.android.internal.util;
+import static java.util.Collections.emptySet;
+
import android.annotation.Nullable;
import android.os.Parcel;
+import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -90,6 +98,132 @@
*/
interface BuiltIn {
+ class ForInternedString implements Parcelling<String> {
+ @Override
+ public void parcel(@Nullable String item, Parcel dest, int parcelFlags) {
+ dest.writeString(item);
+ }
+
+ @Nullable
+ @Override
+ public String unparcel(Parcel source) {
+ return TextUtils.safeIntern(source.readString());
+ }
+ }
+
+ class ForInternedStringArray implements Parcelling<String[]> {
+ @Override
+ public void parcel(String[] item, Parcel dest, int parcelFlags) {
+ dest.writeStringArray(item);
+ }
+
+ @Nullable
+ @Override
+ public String[] unparcel(Parcel source) {
+ String[] array = source.readStringArray();
+ if (array != null) {
+ int size = ArrayUtils.size(array);
+ for (int index = 0; index < size; index++) {
+ array[index] = TextUtils.safeIntern(array[index]);
+ }
+ }
+ return array;
+ }
+ }
+
+ class ForInternedStringList implements Parcelling<List<String>> {
+ @Override
+ public void parcel(List<String> item, Parcel dest, int parcelFlags) {
+ dest.writeStringList(item);
+ }
+
+ @Override
+ public List<String> unparcel(Parcel source) {
+ ArrayList<String> list = source.createStringArrayList();
+ if (list != null) {
+ int size = list.size();
+ for (int index = 0; index < size; index++) {
+ list.set(index, list.get(index).intern());
+ }
+ }
+ return CollectionUtils.emptyIfNull(list);
+ }
+ }
+
+ class ForInternedStringValueMap implements Parcelling<Map<String, String>> {
+ @Override
+ public void parcel(Map<String, String> item, Parcel dest, int parcelFlags) {
+ dest.writeMap(item);
+ }
+
+ @Override
+ public Map<String, String> unparcel(Parcel source) {
+ ArrayMap<String, String> map = new ArrayMap<>();
+ source.readMap(map, String.class.getClassLoader());
+ for (int index = 0; index < map.size(); index++) {
+ map.setValueAt(index, TextUtils.safeIntern(map.valueAt(index)));
+ }
+ return map;
+ }
+ }
+
+ class ForInternedStringSet implements Parcelling<Set<String>> {
+ @Override
+ public void parcel(Set<String> item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(item.size());
+ for (String string : item) {
+ dest.writeString(string);
+ }
+ }
+ }
+
+ @Override
+ public Set<String> unparcel(Parcel source) {
+ final int size = source.readInt();
+ if (size < 0) {
+ return emptySet();
+ }
+ Set<String> set = new ArraySet<>();
+ for (int count = 0; count < size; count++) {
+ set.add(TextUtils.safeIntern(source.readString()));
+ }
+ return set;
+ }
+ }
+
+ class ForBoolean implements Parcelling<Boolean> {
+ @Override
+ public void parcel(@Nullable Boolean item, Parcel dest, int parcelFlags) {
+ if (item == null) {
+ // This writes 1 for null to mirror TypedArray.getInteger(booleanResId, 1)
+ dest.writeInt(1);
+ } else if (!item) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(-1);
+ }
+ }
+
+ @Nullable
+ @Override
+ public Boolean unparcel(Parcel source) {
+ switch (source.readInt()) {
+ default:
+ throw new IllegalStateException("Malformed Parcel reading Boolean: "
+ + source);
+ case 1:
+ return null;
+ case 0:
+ return Boolean.FALSE;
+ case -1:
+ return Boolean.TRUE;
+ }
+ }
+ }
+
class ForPattern implements Parcelling<Pattern> {
@Override
diff --git a/core/java/com/android/internal/util/function/LongObjPredicate.java b/core/java/com/android/internal/util/function/LongObjPredicate.java
new file mode 100644
index 0000000..9e46307
--- /dev/null
+++ b/core/java/com/android/internal/util/function/LongObjPredicate.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2020 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 com.android.internal.util.function;
+
+/**
+ * Represents a predicate (boolean-valued function) of a {@code long}-valued argument
+ * and an object-valued argument.
+ *
+ * @param <T> the type of the object-valued argument to the predicate
+ */
+@FunctionalInterface
+public interface LongObjPredicate<T> {
+ /**
+ * Evaluates this predicate on the given arguments.
+ *
+ * @param value the first input argument
+ * @param t the second input argument
+ * @return {@code true} if the input arguments match the predicate,
+ * otherwise {@code false}
+ */
+ boolean test(long value, T t);
+}
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index 69b1609..633d684 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -210,7 +210,7 @@
}
private boolean isContentRectWithinBounds() {
- mContext.getDisplay().getRealSize(mDisplaySize);
+ mContext.getDisplayNoVerify().getRealSize(mDisplaySize);
mScreenRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
return intersectsClosed(mContentRectOnScreen, mScreenRect)
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 475a321..fd4b5ab 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -16,10 +16,8 @@
package com.android.internal.view;
-import android.content.ComponentName;
import android.os.IBinder;
import android.os.ResultReceiver;
-import android.view.autofill.AutofillId;
import android.view.InputChannel;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
@@ -29,6 +27,7 @@
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
/**
* Top-level interface to an input method component (implemented in a
@@ -38,7 +37,7 @@
oneway interface IInputMethod {
void initializeInternal(IBinder token, int displayId, IInputMethodPrivilegedOperations privOps);
- void onCreateInlineSuggestionsRequest(in ComponentName componentName, in AutofillId autofillId,
+ void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo,
in IInlineSuggestionsRequestCallback cb);
void bindInput(in InputBinding binding);
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 41f902e..4509032 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -28,6 +28,7 @@
void onBindMethod(in InputBindResult res);
void onUnbindMethod(int sequence, int unbindReason);
void setActive(boolean active, boolean fullscreen);
+ void scheduleStartInputIfNecessary(boolean fullscreen);
void reportFullscreenMode(boolean fullscreen);
void reportPreRendered(in EditorInfo info);
void applyImeVisibility(boolean setVisible);
diff --git a/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.aidl b/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.aidl
new file mode 100644
index 0000000..8125c0d
--- /dev/null
+++ b/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.view;
+
+parcelable InlineSuggestionsRequestInfo;
diff --git a/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.java b/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.java
new file mode 100644
index 0000000..6148490
--- /dev/null
+++ b/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.view;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Wraps the information needed to create an {@link InlineSuggestionsRequest}.
+ *
+ * @hide
+ */
+@DataClass(
+ genToString = true,
+ genHiddenConstDefs = true,
+ genEqualsHashCode = true)
+public final class InlineSuggestionsRequestInfo implements Parcelable {
+ /**
+ * The {@link ComponentName} of current app/activity
+ */
+ private final @NonNull ComponentName mComponentName;
+
+ /**
+ * The {@link AutofillId} of currently focused field.
+ */
+ private final @NonNull AutofillId mAutofillId;
+
+ /**
+ * The extras that contain the UI renderer related information
+ */
+ private final @NonNull Bundle mUiExtras;
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new InlineSuggestionsRequestInfo.
+ *
+ * @param componentName
+ * The {@link ComponentName} of current app/activity
+ * @param autofillId
+ * The {@link AutofillId} of currently focused field.
+ * @param uiExtras
+ * The extras that contain the ui renderer related information
+ */
+ @DataClass.Generated.Member
+ public InlineSuggestionsRequestInfo(
+ @NonNull ComponentName componentName,
+ @NonNull AutofillId autofillId,
+ @NonNull Bundle uiExtras) {
+ this.mComponentName = componentName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mComponentName);
+ this.mAutofillId = autofillId;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAutofillId);
+ this.mUiExtras = uiExtras;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mUiExtras);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The {@link ComponentName} of current app/activity
+ */
+ @DataClass.Generated.Member
+ public @NonNull ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ /**
+ * The {@link AutofillId} of currently focused field.
+ */
+ @DataClass.Generated.Member
+ public @NonNull AutofillId getAutofillId() {
+ return mAutofillId;
+ }
+
+ /**
+ * The extras that contain the ui renderer related information
+ */
+ @DataClass.Generated.Member
+ public @NonNull Bundle getUiExtras() {
+ return mUiExtras;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "InlineSuggestionsRequestInfo { " +
+ "componentName = " + mComponentName + ", " +
+ "autofillId = " + mAutofillId + ", " +
+ "uiExtras = " + mUiExtras +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(InlineSuggestionsRequestInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ InlineSuggestionsRequestInfo that = (InlineSuggestionsRequestInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mComponentName, that.mComponentName)
+ && java.util.Objects.equals(mAutofillId, that.mAutofillId)
+ && java.util.Objects.equals(mUiExtras, that.mUiExtras);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mComponentName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAutofillId);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mUiExtras);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeTypedObject(mComponentName, flags);
+ dest.writeTypedObject(mAutofillId, flags);
+ dest.writeBundle(mUiExtras);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ InlineSuggestionsRequestInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ ComponentName componentName = (ComponentName) in.readTypedObject(ComponentName.CREATOR);
+ AutofillId autofillId = (AutofillId) in.readTypedObject(AutofillId.CREATOR);
+ Bundle uiExtras = in.readBundle();
+
+ this.mComponentName = componentName;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mComponentName);
+ this.mAutofillId = autofillId;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAutofillId);
+ this.mUiExtras = uiExtras;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mUiExtras);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<InlineSuggestionsRequestInfo> CREATOR
+ = new Parcelable.Creator<InlineSuggestionsRequestInfo>() {
+ @Override
+ public InlineSuggestionsRequestInfo[] newArray(int size) {
+ return new InlineSuggestionsRequestInfo[size];
+ }
+
+ @Override
+ public InlineSuggestionsRequestInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new InlineSuggestionsRequestInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1582076613213L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/core/java/com/android/internal/view/InlineSuggestionsRequestInfo.java",
+ inputSignatures = "private final @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.NonNull android.os.Bundle mUiExtras\nclass InlineSuggestionsRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index a5964b5..f29e95c 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -89,57 +89,64 @@
*/
int SUCCESS_WAITING_IME_BINDING = 2;
/**
+ * Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} has a
+ * pending operation to switch to a different user.
+ *
+ * <p>Note that in this state even what would be the next current IME is not determined.</p>
+ */
+ int SUCCESS_WAITING_USER_SWITCHING = 3;
+ /**
* Indicates that this is not intended for starting input but just for reporting window
* focus change from the application process.
*
* <p>All other fields do not have meaningful value.</p>
*/
- int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 3;
+ int SUCCESS_REPORT_WINDOW_FOCUS_ONLY = 4;
/**
* Indicates somehow
* {@link
* com.android.server.inputmethod.InputMethodManagerService#startInputOrWindowGainedFocus}
* is trying to return null {@link InputBindResult}, which must never happen.
*/
- int ERROR_NULL = 4;
+ int ERROR_NULL = 5;
/**
* Indicates that {@link com.android.server.inputmethod.InputMethodManagerService}
* recognizes no IME.
*/
- int ERROR_NO_IME = 5;
+ int ERROR_NO_IME = 6;
/**
* Indicates that {@link android.view.inputmethod.EditorInfo#packageName} does not match
* the caller UID.
*
* @see android.view.inputmethod.EditorInfo#packageName
*/
- int ERROR_INVALID_PACKAGE_NAME = 6;
+ int ERROR_INVALID_PACKAGE_NAME = 7;
/**
* Indicates that the system is still in an early stage of the boot process and any 3rd
* party application is not allowed to run.
*
* @see com.android.server.SystemService#PHASE_THIRD_PARTY_APPS_CAN_START
*/
- int ERROR_SYSTEM_NOT_READY = 7;
+ int ERROR_SYSTEM_NOT_READY = 8;
/**
* Indicates that {@link com.android.server.inputmethod.InputMethodManagerService} tried to
* connect to an {@link android.inputmethodservice.InputMethodService} but failed.
*
* @see android.content.Context#bindServiceAsUser(Intent, ServiceConnection, int, UserHandle)
*/
- int ERROR_IME_NOT_CONNECTED = 8;
+ int ERROR_IME_NOT_CONNECTED = 9;
/**
* Indicates that the caller is not the foreground user, does not have
* {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission, or the user
* specified in {@link android.view.inputmethod.EditorInfo#targetInputMethodUser} is not
* running.
*/
- int ERROR_INVALID_USER = 9;
+ int ERROR_INVALID_USER = 10;
/**
* Indicates that the caller should have specified non-null
* {@link android.view.inputmethod.EditorInfo}.
*/
- int ERROR_NULL_EDITOR_INFO = 10;
+ int ERROR_NULL_EDITOR_INFO = 11;
/**
* Indicates that the target window the client specified cannot be the IME target right now.
*
@@ -149,24 +156,24 @@
*
* @see com.android.server.wm.WindowManagerInternal#isInputMethodClientFocus(int, int, int)
*/
- int ERROR_NOT_IME_TARGET_WINDOW = 11;
+ int ERROR_NOT_IME_TARGET_WINDOW = 12;
/**
* Indicates that focused view in the current window is not an editor.
*/
- int ERROR_NO_EDITOR = 12;
+ int ERROR_NO_EDITOR = 13;
/**
* Indicates that there is a mismatch in display ID between IME client and focused Window.
*/
- int ERROR_DISPLAY_ID_MISMATCH = 13;
+ int ERROR_DISPLAY_ID_MISMATCH = 14;
/**
* Indicates that current IME client is no longer allowed to access to the associated
* display.
*/
- int ERROR_INVALID_DISPLAY_ID = 14;
+ int ERROR_INVALID_DISPLAY_ID = 15;
/**
* Indicates that the client is not recognized by the system.
*/
- int ERROR_INVALID_CLIENT = 15;
+ int ERROR_INVALID_CLIENT = 16;
}
@ResultCode
@@ -299,6 +306,8 @@
return "SUCCESS_WAITING_IME_SESSION";
case ResultCode.SUCCESS_WAITING_IME_BINDING:
return "SUCCESS_WAITING_IME_BINDING";
+ case ResultCode.SUCCESS_WAITING_USER_SWITCHING:
+ return "SUCCESS_WAITING_USER_SWITCHING";
case ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY:
return "SUCCESS_REPORT_WINDOW_FOCUS_ONLY";
case ResultCode.ERROR_NULL:
@@ -386,4 +395,11 @@
* Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
*/
public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
+
+ /**
+ * Predefined <strong>success</strong> object for
+ * {@link ResultCode#SUCCESS_WAITING_USER_SWITCHING}.
+ */
+ public static final InputBindResult USER_SWITCHING =
+ error(ResultCode.SUCCESS_WAITING_USER_SWITCHING);
}
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index 13425e5..cfb2bf9 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -77,10 +77,7 @@
final Point size = new Point();
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
- final Display display = context.getDisplay();
- final int displayId = display != null
- ? display.getDisplayId()
- : Display.DEFAULT_DISPLAY;
+ final int displayId = context.getDisplayId();
wm.getInitialDisplaySize(displayId, size);
return size.x < size.y ?
Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index e24e982..e35fda1 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -57,6 +57,8 @@
void registerStrongAuthTracker(in IStrongAuthTracker tracker);
void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
void requireStrongAuth(int strongAuthReason, int userId);
+ void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId);
+ void scheduleNonStrongBiometricIdleTimeout(int userId);
void systemReady();
void userPresent(int userId);
int getStrongAuthForUser(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 864429c..d9b2902 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -49,6 +49,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
@@ -1382,6 +1383,22 @@
}
}
+ public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ try {
+ getLockSettings().reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not report successful biometric unlock", e);
+ }
+ }
+
+ public void scheduleNonStrongBiometricIdleTimeout(int userId) {
+ try {
+ getLockSettings().scheduleNonStrongBiometricIdleTimeout(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not schedule non-strong biometric idle timeout", e);
+ }
+ }
+
/**
* @see StrongAuthTracker#getStrongAuthForUser
*/
@@ -1557,7 +1574,8 @@
SOME_AUTH_REQUIRED_AFTER_USER_REQUEST,
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN})
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+ STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1604,6 +1622,12 @@
public static final int STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE = 0x40;
/**
+ * Strong authentication is required because it hasn't been used for a time after a
+ * non-strong biometric (i.e. weak or convenience biometric) is used to unlock device.
+ */
+ public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80;
+
+ /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
@@ -1614,6 +1638,10 @@
private final H mHandler;
private final int mDefaultStrongAuthFlags;
+ private final SparseBooleanArray mIsNonStrongBiometricAllowedForUser =
+ new SparseBooleanArray();
+ private final boolean mDefaultIsNonStrongBiometricAllowed = true;
+
public StrongAuthTracker(Context context) {
this(context, Looper.myLooper());
}
@@ -1657,8 +1685,21 @@
* @return true if unlocking with a biometric method alone is allowed for {@code userId}
* by the current strong authentication requirements.
*/
- public boolean isBiometricAllowedForUser(int userId) {
- return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0;
+ public boolean isBiometricAllowedForUser(boolean isStrongBiometric, int userId) {
+ boolean allowed = ((getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0);
+ if (!isStrongBiometric) {
+ allowed &= isNonStrongBiometricAllowedAfterIdleTimeout(userId);
+ }
+ return allowed;
+ }
+
+ /**
+ * @return true if unlocking with a non-strong (i.e. weak or convenience) biometric method
+ * alone is allowed for {@code userId}, otherwise returns false.
+ */
+ public boolean isNonStrongBiometricAllowedAfterIdleTimeout(int userId) {
+ return mIsNonStrongBiometricAllowedForUser.get(userId,
+ mDefaultIsNonStrongBiometricAllowed);
}
/**
@@ -1667,6 +1708,12 @@
public void onStrongAuthRequiredChanged(int userId) {
}
+ /**
+ * Called when whether non-strong biometric is allowed for {@code userId} changed.
+ */
+ public void onIsNonStrongBiometricAllowedChanged(int userId) {
+ }
+
protected void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
int userId) {
int oldValue = getStrongAuthForUser(userId);
@@ -1680,6 +1727,18 @@
}
}
+ protected void handleIsNonStrongBiometricAllowedChanged(boolean allowed,
+ int userId) {
+ boolean oldValue = isNonStrongBiometricAllowedAfterIdleTimeout(userId);
+ if (allowed != oldValue) {
+ if (allowed == mDefaultIsNonStrongBiometricAllowed) {
+ mIsNonStrongBiometricAllowedForUser.delete(userId);
+ } else {
+ mIsNonStrongBiometricAllowedForUser.put(userId, allowed);
+ }
+ onIsNonStrongBiometricAllowedChanged(userId);
+ }
+ }
protected final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() {
@Override
@@ -1688,10 +1747,17 @@
mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED,
strongAuthFlags, userId).sendToTarget();
}
+
+ @Override
+ public void onIsNonStrongBiometricAllowedChanged(boolean allowed, int userId) {
+ mHandler.obtainMessage(H.MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED,
+ allowed ? 1 : 0, userId).sendToTarget();
+ }
};
private class H extends Handler {
static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1;
+ static final int MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED = 2;
public H(Looper looper) {
super(looper);
@@ -1703,6 +1769,10 @@
case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED:
handleStrongAuthRequiredChanged(msg.arg1, msg.arg2);
break;
+ case MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED:
+ handleIsNonStrongBiometricAllowedChanged(msg.arg1 == 1 /* allowed */,
+ msg.arg2);
+ break;
}
}
}
diff --git a/core/jni/android_os_storage_StorageManager.cpp b/core/jni/android_os_storage_StorageManager.cpp
index aee6733..fd3e66b 100644
--- a/core/jni/android_os_storage_StorageManager.cpp
+++ b/core/jni/android_os_storage_StorageManager.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "StorageManager"
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <fcntl.h>
@@ -25,11 +26,30 @@
namespace android {
+static const char* kProcFilesystems = "/proc/filesystems";
+
+// Checks whether the passed in filesystem is listed in /proc/filesystems
+static bool IsFilesystemSupported(const std::string& fsType) {
+ std::string supported;
+ if (!android::base::ReadFileToString(kProcFilesystems, &supported)) {
+ PLOG(ERROR) << "Failed to read supported filesystems";
+ return false;
+ }
+ return supported.find(fsType + "\n") != std::string::npos;
+}
+
jboolean android_os_storage_StorageManager_setQuotaProjectId(JNIEnv* env, jobject self,
jstring path, jlong projectId) {
struct fsxattr fsx;
ScopedUtfChars utf_chars_path(env, path);
+ static bool sdcardFsSupported = IsFilesystemSupported("sdcardfs");
+ if (sdcardFsSupported) {
+ // sdcardfs doesn't support project ID quota tracking and takes care of quota
+ // in a different way.
+ return JNI_TRUE;
+ }
+
if (projectId > UINT32_MAX) {
LOG(ERROR) << "Invalid project id: " << projectId;
return JNI_FALSE;
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 27c5a73..93449ff 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -186,6 +186,7 @@
proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
}
proxy->setSurface(window, enableTimeout);
+ ANativeWindow_release(window);
}
static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index f03f427..8bb4d50 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -47,28 +47,32 @@
static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,
const char* field_signature) {
jfieldID res = env->GetFieldID(clazz, field_name, field_signature);
- LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name);
+ LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name,
+ field_signature);
return res;
}
static inline jmethodID GetMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,
const char* method_signature) {
jmethodID res = env->GetMethodID(clazz, method_name, method_signature);
- LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s", method_name);
+ LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find method %s with signature %s", method_name,
+ method_signature);
return res;
}
static inline jfieldID GetStaticFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name,
const char* field_signature) {
jfieldID res = env->GetStaticFieldID(clazz, field_name, field_signature);
- LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s", field_name);
+ LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name,
+ field_signature);
return res;
}
static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name,
const char* method_signature) {
jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature);
- LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s", method_name);
+ LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s",
+ method_name, method_signature);
return res;
}
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
index e476c52..e65a2ab 100644
--- a/core/proto/android/server/peopleservice.proto
+++ b/core/proto/android/server/peopleservice.proto
@@ -50,6 +50,9 @@
// Integer representation of conversation bit flags.
optional int32 conversation_flags = 6;
+
+ // The phone number of the contact.
+ optional string contact_phone_number = 7;
}
// On disk data of events.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 62e57e4..814b8ac 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -399,6 +399,9 @@
<protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
<protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
<protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_STATUS" />
<!-- Legacy -->
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
@@ -2771,7 +2774,7 @@
<permission android:name="android.permission.READ_DEVICE_CONFIG"
android:protectionLevel="signature|preinstalled" />
- <!-- @SystemApi @hide Allows an application to monitor config settings access.
+ <!-- @hide Allows an application to monitor {@link android.provider.Settings.Config} access.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
android:protectionLevel="signature"/>
@@ -3081,8 +3084,7 @@
<!-- Allows SystemUI to request third party controls.
<p>Should only be requested by the System and required by
- ControlsService declarations.
- @hide
+ {@link android.service.controls.ControlsProviderService} declarations.
-->
<permission android:name="android.permission.BIND_CONTROLS"
android:protectionLevel="signature" />
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index c0de693..5676049 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -90,7 +90,7 @@
android:id="@android:id/tabcontent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <com.android.internal.app.ResolverViewPager
+ <com.android.internal.widget.ViewPager
android:id="@+id/profile_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c15b794..70d8a02 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2460,6 +2460,12 @@
rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max -->
<string name="config_ethernet_tcp_buffers" translatable="false">524288,1048576,3145728,524288,1048576,2097152</string>
+ <!-- What source to use to estimate link upstream and downstream bandwidth capacities.
+ Default is carrier_config, but it should be set to modem if the modem is returning
+ predictive (instead of instantaneous) bandwidth estimate.
+ Values are carrier_config and modem. -->
+ <string name="config_bandwidthEstimateSource">carrier_config</string>
+
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
Here are a few of them:
@@ -2582,6 +2588,18 @@
<string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
+ <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist
+ a wireless network for wireless debugging.
+ Can be customized for other product types -->
+ <string name="config_customAdbWifiNetworkConfirmationComponent"
+ >com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string>
+
+ <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to
+ switch to the primary user to enable wireless debugging.
+ Can be customized for other product types -->
+ <string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent"
+ >com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
+
<!-- Component name of the activity that shows the usb containment status. -->
<string name="config_usbContaminantActivity" translatable="false"
>com.android.systemui/com.android.systemui.usb.UsbContaminantActivity</string>
@@ -3532,6 +3550,8 @@
mode -->
<string-array translatable="false" name="config_priorityOnlyDndExemptPackages">
<item>com.android.dialer</item>
+ <item>com.android.systemui</item>
+ <item>android</item>
</string-array>
<!-- The default value for transition animation scale found in developer settings.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 48049b4..4f221d0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -781,9 +781,6 @@
<dimen name="chooser_action_button_icon_size">18dp</dimen>
- <!-- Assistant handles -->
- <dimen name="assist_handle_shadow_radius">2dp</dimen>
-
<!-- For Waterfall Display -->
<dimen name="waterfall_display_left_edge_size">0px</dimen>
<dimen name="waterfall_display_top_edge_size">0px</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d513e2b..1445736 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3527,6 +3527,12 @@
<!-- Message of notification shown when ADB is actively connected to the phone. -->
<string name="adb_active_notification_message">Tap to turn off USB debugging</string>
<string name="adb_active_notification_message" product="tv">Select to disable USB debugging.</string>
+ <!-- Title of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] -->
+ <string name="adbwifi_active_notification_title">Wireless debugging connected</string>
+ <!-- Message of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] -->
+ <string name="adbwifi_active_notification_message">Tap to turn off wireless debugging</string>
+ <!-- Message of notification shown when ADB Wireless is actively connected to the TV. [CHAR LIMIT=NONE] -->
+ <string name="adbwifi_active_notification_message" product="tv">Select to disable wireless debugging.</string>
<!-- Title of notification shown when Test Harness Mode is enabled. [CHAR LIMIT=NONE] -->
<string name="test_harness_mode_notification_title">Test Harness Mode enabled</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 85c2a2a..a3da411 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -473,6 +473,7 @@
<java-symbol type="integer" name="config_num_physical_slots" />
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
+ <java-symbol type="string" name="config_bandwidthEstimateSource" />
<java-symbol type="color" name="tab_indicator_text_v4" />
@@ -2008,6 +2009,8 @@
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
+ <java-symbol type="string" name="adbwifi_active_notification_message" />
+ <java-symbol type="string" name="adbwifi_active_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_message" />
<java-symbol type="string" name="console_running_notification_title" />
@@ -2153,6 +2156,8 @@
<java-symbol type="integer" name="config_attentiveWarningDuration" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
+ <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationComponent" />
+ <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
<java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
@@ -3839,9 +3844,6 @@
<!-- For App Standby -->
<java-symbol type="string" name="as_app_forced_to_restricted_bucket" />
- <!-- Assistant handles -->
- <java-symbol type="dimen" name="assist_handle_shadow_radius" />
-
<!-- For Waterfall Display -->
<java-symbol type="dimen" name="waterfall_display_left_edge_size" />
<java-symbol type="dimen" name="waterfall_display_top_edge_size" />
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 899d630..d8ec72f 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -51,6 +51,12 @@
<value>0.1</value> <!-- ~1mA -->
</array>
+ <!-- Additional power consumption by CPU excluding cluster and core when
+ running -->
+ <array name="cpu.active">
+ <value>0.1</value>
+ </array>
+
<!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
number of CPU cores for that cluster.
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 8e4e684..33fead6 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -83,21 +83,13 @@
":FrameworksCoreTests_install_bad_dex",
":FrameworksCoreTests_install_complete_package_info",
":FrameworksCoreTests_install_decl_perm",
- ":FrameworksCoreTests_install_intent_filters",
":FrameworksCoreTests_install_jni_lib_open_from_apk",
":FrameworksCoreTests_install_loc_auto",
":FrameworksCoreTests_install_loc_internal",
":FrameworksCoreTests_install_loc_sdcard",
":FrameworksCoreTests_install_loc_unspecified",
- ":FrameworksCoreTests_install_split_base",
- ":FrameworksCoreTests_install_split_feature_a",
":FrameworksCoreTests_install_use_perm_good",
":FrameworksCoreTests_install_uses_feature",
- ":FrameworksCoreTests_install_uses_sdk_0",
- ":FrameworksCoreTests_install_uses_sdk_q0",
- ":FrameworksCoreTests_install_uses_sdk_r",
- ":FrameworksCoreTests_install_uses_sdk_r0",
- ":FrameworksCoreTests_install_uses_sdk_r5",
":FrameworksCoreTests_install_verifier_bad",
":FrameworksCoreTests_install_verifier_good",
":FrameworksCoreTests_keyset_permdef_sa_unone",
diff --git a/core/tests/coretests/apks/install-split-base/Android.bp b/core/tests/coretests/apks/install-split-base/Android.bp
deleted file mode 100644
index ddf75b2..0000000
--- a/core/tests/coretests/apks/install-split-base/Android.bp
+++ /dev/null
@@ -1,6 +0,0 @@
-android_test_helper_app {
- name: "FrameworksCoreTests_install_split_base",
- defaults: ["FrameworksCoreTests_apks_defaults"],
-
- srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/apks/install_intent_filters/Android.bp b/core/tests/coretests/apks/install_intent_filters/Android.bp
deleted file mode 100644
index 6cc5eba..0000000
--- a/core/tests/coretests/apks/install_intent_filters/Android.bp
+++ /dev/null
@@ -1,7 +0,0 @@
-android_test_helper_app {
- name: "FrameworksCoreTests_install_intent_filters",
- defaults: ["FrameworksCoreTests_apks_defaults"],
-
- srcs: ["**/*.java"],
-}
-
diff --git a/core/tests/coretests/apks/install_uses_sdk/Android.bp b/core/tests/coretests/apks/install_uses_sdk/Android.bp
deleted file mode 100644
index 92b09ed..0000000
--- a/core/tests/coretests/apks/install_uses_sdk/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-android_test_helper_app {
- name: "FrameworksCoreTests_install_uses_sdk_r0",
- defaults: ["FrameworksCoreTests_apks_defaults"],
- manifest: "AndroidManifest-r0.xml",
-
- srcs: ["**/*.java"],
-}
-
-android_test_helper_app {
- name: "FrameworksCoreTests_install_uses_sdk_r5",
- defaults: ["FrameworksCoreTests_apks_defaults"],
- manifest: "AndroidManifest-r5.xml",
-
- srcs: ["**/*.java"],
-}
-
-android_test_helper_app {
- name: "FrameworksCoreTests_install_uses_sdk_q0",
- defaults: ["FrameworksCoreTests_apks_defaults"],
- manifest: "AndroidManifest-q0.xml",
-
- srcs: ["**/*.java"],
-}
-
-android_test_helper_app {
- name: "FrameworksCoreTests_install_uses_sdk_r",
- defaults: ["FrameworksCoreTests_apks_defaults"],
- manifest: "AndroidManifest-r.xml",
-
- srcs: ["**/*.java"],
-}
-
-android_test_helper_app {
- name: "FrameworksCoreTests_install_uses_sdk_0",
- defaults: ["FrameworksCoreTests_apks_defaults"],
- manifest: "AndroidManifest-0.xml",
-
- srcs: ["**/*.java"],
-}
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index 82a7b2c..f0e70a5 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -90,6 +90,12 @@
}
@Test
+ public void testLoadAnimatedImage() {
+ assertNotNull("Can't find animated image",
+ mShortcutInfo.loadAnimatedImage(mTargetContext));
+ }
+
+ @Test
public void testHtmlDescription() {
final String htmlDescription = mTargetContext.getResources()
.getString(R.string.accessibility_shortcut_html_description);
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 78c4420..1737bd0 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -234,4 +234,12 @@
assertThat(type).isNull();
assertThat(end).isLessThan(start + 5000);
}
+
+ @Test
+ public void testCanonicalize() {
+ Uri canonical = mResolver.canonicalize(
+ Uri.parse("content://android.content.FakeProviderRemote/something"));
+ assertThat(canonical).isEqualTo(
+ Uri.parse("content://android.content.FakeProviderRemote/canonical"));
+ }
}
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index f074233..f0997a6 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -16,11 +16,13 @@
package android.content;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static org.junit.Assert.assertEquals;
import android.app.ActivityThread;
+import android.hardware.display.DisplayManager;
import android.os.UserHandle;
-import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -45,17 +47,16 @@
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
- assertEquals(testContext.getDisplay().getDisplayId(), testContext.getDisplayId());
+ assertEquals(testContext.getDisplayNoVerify().getDisplayId(), testContext.getDisplayId());
}
- // TODO(b/128338354): Re-visit this test after introducing WindowContext
@Test
public void testDisplayIdForDefaultDisplayContext() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
- final WindowManager wm = testContext.getSystemService(WindowManager.class);
+ final DisplayManager dm = testContext.getSystemService(DisplayManager.class);
final Context defaultDisplayContext =
- testContext.createDisplayContext(wm.getDefaultDisplay());
+ testContext.createDisplayContext(dm.getDisplay(DEFAULT_DISPLAY));
assertEquals(defaultDisplayContext.getDisplay().getDisplayId(),
defaultDisplayContext.getDisplayId());
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 7b9bdbc..1d7ba5d 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -54,4 +54,10 @@
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
+
+ @Override
+ public Uri canonicalize(Uri uri) {
+ return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority())
+ .appendPath("canonical").build();
+ }
}
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
index 62c9c98..7e4c138 100644
--- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
@@ -109,8 +109,8 @@
@Test
public void createGreaterThanOrEqualsToFormula_versionCode() {
int versionCode = 12;
- IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo(
- versionCode);
+ IntegrityFormula formula =
+ IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo(versionCode);
AtomicFormula.LongAtomicFormula stringAtomicFormula =
(AtomicFormula.LongAtomicFormula) formula;
@@ -124,11 +124,11 @@
public void createIsTrueFormula_preInstalled() {
IntegrityFormula formula = IntegrityFormula.Application.isPreInstalled();
- AtomicFormula.BooleanAtomicFormula stringAtomicFormula =
+ AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
(AtomicFormula.BooleanAtomicFormula) formula;
- assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PRE_INSTALLED);
- assertThat(stringAtomicFormula.getValue()).isTrue();
+ assertThat(booleanAtomicFormula.getKey()).isEqualTo(AtomicFormula.PRE_INSTALLED);
+ assertThat(booleanAtomicFormula.getValue()).isTrue();
}
@Test
@@ -136,8 +136,8 @@
String packageName = "com.test.package";
String certificateName = "certificate";
IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName);
- IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain(
- certificateName);
+ IntegrityFormula formula2 =
+ IntegrityFormula.Application.certificatesContain(certificateName);
IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2);
@@ -149,8 +149,8 @@
String packageName = "com.test.package";
String certificateName = "certificate";
IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName);
- IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain(
- certificateName);
+ IntegrityFormula formula2 =
+ IntegrityFormula.Application.certificatesContain(certificateName);
IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2);
@@ -166,4 +166,29 @@
assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG);
}
+
+ @Test
+ public void createIsTrueFormula_stampNotTrusted() {
+ IntegrityFormula formula = IntegrityFormula.SourceStamp.notTrusted();
+
+ AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
+ (AtomicFormula.BooleanAtomicFormula) formula;
+
+ assertThat(booleanAtomicFormula.getKey()).isEqualTo(AtomicFormula.STAMP_TRUSTED);
+ assertThat(booleanAtomicFormula.getValue()).isFalse();
+ }
+
+ @Test
+ public void createEqualsFormula_stampCertificateHash() {
+ String stampCertificateHash = "test-cert";
+ IntegrityFormula formula =
+ IntegrityFormula.SourceStamp.stampCertificateHashEquals(stampCertificateHash);
+
+ AtomicFormula.StringAtomicFormula stringAtomicFormula =
+ (AtomicFormula.StringAtomicFormula) formula;
+
+ assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.STAMP_CERTIFICATE_HASH);
+ assertThat(stringAtomicFormula.getValue()).matches(stampCertificateHash);
+ assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
+ }
}
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 78c88d7..4c2ca7e 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -25,9 +25,13 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -44,6 +48,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -59,6 +64,11 @@
@RunWith(AndroidJUnit4.class)
public class ControlProviderServiceTest {
+ private static final ComponentName TEST_SYSUI_COMPONENT =
+ ComponentName.unflattenFromString("sysui/.test.cls");
+ private static final ComponentName TEST_COMPONENT =
+ ComponentName.unflattenFromString("test.pkg/.test.cls");
+
private IBinder mToken = new Binder();
@Mock
private IControlsActionCallback.Stub mActionCallback;
@@ -66,6 +76,12 @@
private IControlsSubscriber.Stub mSubscriber;
@Mock
private IIntentSender mIIntentSender;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private Context mContext;
+ @Captor
+ private ArgumentCaptor<Intent> mIntentArgumentCaptor;
private PendingIntent mPendingIntent;
private FakeControlsProviderService mControlsProviderService;
@@ -81,6 +97,10 @@
when(mSubscriber.asBinder()).thenCallRealMethod();
when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);
+ when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent))
+ .thenReturn(TEST_SYSUI_COMPONENT.flattenToString());
+ when(mContext.getResources()).thenReturn(mResources);
+
Bundle b = new Bundle();
b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken);
Intent intent = new Intent();
@@ -223,6 +243,21 @@
ControlAction.RESPONSE_OK);
}
+ @Test
+ public void testRequestAdd() {
+ Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build();
+ ControlsProviderService.requestAddControl(mContext, TEST_COMPONENT, control);
+
+ verify(mContext).sendBroadcast(mIntentArgumentCaptor.capture(),
+ eq(Manifest.permission.BIND_CONTROLS));
+ Intent intent = mIntentArgumentCaptor.getValue();
+ assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction());
+ assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage());
+ assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME));
+ assertTrue(equals(control,
+ intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
+ }
+
private static boolean equals(Control c1, Control c2) {
if (c1 == c2) return true;
if (c1 == null || c2 == null) return false;
diff --git a/core/tests/coretests/src/android/util/EventLogTest.java b/core/tests/coretests/src/android/util/EventLogTest.java
new file mode 100644
index 0000000..94e72c4
--- /dev/null
+++ b/core/tests/coretests/src/android/util/EventLogTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.util;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.util.EventLog.Event;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Unit tests for {@link android.util.EventLog} */
+public class EventLogTest {
+
+ @Test
+ public void testWithNewData() throws Throwable {
+ Event event = createEvent(() -> {
+ EventLog.writeEvent(314, 123);
+ }, 314);
+
+ assertTrue(event.withNewData(12345678L).getData().equals(12345678L));
+ assertTrue(event.withNewData(2.718f).getData().equals(2.718f));
+ assertTrue(event.withNewData("test string").getData().equals("test string"));
+
+ Object[] objects = ((Object[]) event.withNewData(
+ new Object[] {111, 2.22f, 333L, "444"}).getData());
+ assertEquals(4, objects.length);
+ assertTrue(objects[0].equals(111));
+ assertTrue(objects[1].equals(2.22f));
+ assertTrue(objects[2].equals(333L));
+ assertTrue(objects[3].equals("444"));
+ }
+
+ /**
+ * Creates an Event object. Only the native code has the serialization and deserialization logic
+ * so need to actually emit a real log in order to generate the object.
+ */
+ private Event createEvent(Runnable generator, int expectedTag) throws Exception {
+ Long markerData = System.currentTimeMillis();
+ EventLog.writeEvent(expectedTag, markerData);
+ generator.run();
+
+ List<Event> events = new ArrayList<>();
+ // Give the message some time to show up in the log
+ Thread.sleep(20);
+ EventLog.readEvents(new int[] {expectedTag}, events);
+ for (int i = 0; i < events.size() - 1; i++) {
+ if (markerData.equals(events.get(i).getData())) {
+ return events.get(i + 1);
+ }
+ }
+ throw new AssertionFailedError("Unable to locate marker event");
+ }
+}
diff --git a/core/tests/coretests/src/android/util/LongSparseArrayTest.java b/core/tests/coretests/src/android/util/LongSparseArrayTest.java
new file mode 100644
index 0000000..bf3f0f5
--- /dev/null
+++ b/core/tests/coretests/src/android/util/LongSparseArrayTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.function.LongObjPredicate;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link LongSparseArray}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LongSparseArrayTest {
+ @Test
+ public void testRemoveIf() {
+ final LongSparseArray<Integer> sparseArray = new LongSparseArray();
+ for (int i = 0; i < 10; ++i) {
+ for (int j = 100; j < 110; ++j) {
+ sparseArray.put(i, j);
+ sparseArray.put(-i, j);
+ sparseArray.put(j, -i);
+ sparseArray.put(-j, -i);
+ }
+ }
+
+ final LongObjPredicate<Integer> predicate = (value, obj) -> (value < 0 && obj < 0);
+ sparseArray.removeIf(predicate);
+
+ for (int i = 0; i < sparseArray.size(); ++i) {
+ assertThat(predicate.test(sparseArray.keyAt(i), sparseArray.valueAt(i)))
+ .isFalse();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
index b41f90c..7872810 100644
--- a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -21,10 +21,27 @@
import static org.testng.Assert.assertThrows;
import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+/**
+ * Tests for {@link CutoutSpecification} used by {@link DisplayCutout}.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:CutoutSpecificationTest
+ *
+ * <p>This test class is a part of Window Manager Service tests and specified in
+ * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
public class CutoutSpecificationTest {
private static final String WITHOUT_BIND_CUTOUT_SPECIFICATION = "M 0,0\n"
+ "h 48\n"
@@ -344,7 +361,7 @@
.parse("@bottom"
+ "M 0,0\n"
+ "v -10\n"
- + "h 10\n"
+ + "h -10\n"
+ "v 10\n"
+ "z\n"
+ "@right\n"
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 7c78bce..7f0e0d2 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -61,7 +61,7 @@
.setName("testSurface")
.build();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplay());
+ ViewRootImpl viewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify());
try {
viewRootImpl.setView(new TextView(mContext), new LayoutParams(), null);
} catch (BadTokenException e) {
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 24fe2a0..7737b1a 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -110,7 +110,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
Context context = InstrumentationRegistry.getTargetContext();
// cannot mock ViewRootImpl since it's final.
- mViewRoot = new ViewRootImpl(context, context.getDisplay());
+ mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify());
try {
mViewRoot.setView(new TextView(context), new LayoutParams(), null);
} catch (BadTokenException e) {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 5e9e2f0..754c679 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -77,7 +77,8 @@
instrumentation.runOnMainSync(() -> {
final Context context = instrumentation.getTargetContext();
// cannot mock ViewRootImpl since it's final.
- final ViewRootImpl viewRootImpl = new ViewRootImpl(context, context.getDisplay());
+ final ViewRootImpl viewRootImpl = new ViewRootImpl(context,
+ context.getDisplayNoVerify());
try {
viewRootImpl.setView(new TextView(context), new LayoutParams(), null);
} catch (BadTokenException e) {
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index df6ed8c..e2adbcc 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -61,7 +61,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mViewRootImpl = new ViewRootImplAccessor(
- new ViewRootImpl(mContext, mContext.getDisplay()));
+ new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
});
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index d7abfcc..0a094c61d 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -50,7 +50,6 @@
import com.google.common.base.Strings;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -69,7 +68,6 @@
public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>(
TextViewActivity.class);
- private boolean mOriginalFlagValue;
private Instrumentation mInstrumentation;
private Activity mActivity;
@@ -77,13 +75,6 @@
public void before() throws Throwable {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
- mOriginalFlagValue = Editor.FLAG_ENABLE_CURSOR_DRAG;
- Editor.FLAG_ENABLE_CURSOR_DRAG = true;
- }
-
- @After
- public void after() throws Throwable {
- Editor.FLAG_ENABLE_CURSOR_DRAG = mOriginalFlagValue;
}
@Test
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 0c38e71..88a6f9e 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -31,8 +31,8 @@
import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
import static android.widget.espresso.TextViewActions.doubleTapHandle;
import static android.widget.espresso.TextViewActions.dragHandle;
-import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
import static android.widget.espresso.TextViewActions.longPressAndDragHandle;
+import static android.widget.espresso.TextViewActions.longPressAndDragOnText;
import static android.widget.espresso.TextViewActions.longPressHandle;
import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
import static android.widget.espresso.TextViewAssertions.doesNotHaveStyledText;
@@ -514,29 +514,26 @@
onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
}
+ private void enableFlagsForInsertionHandleGestures() {
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ final Editor editor = textView.getEditorForTesting();
+ editor.setFlagCursorDragFromAnywhereEnabled(true);
+ editor.setFlagInsertionHandleGesturesEnabled(true);
+ // Note: We don't need to reset these flags explicitly at the end of each test, because a
+ // fresh TextView and Editor will be created for each test.
+ }
+
@Test
public void testInsertionHandle_touchThrough() {
- final TextView textView = mActivity.findViewById(R.id.textview);
- boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled();
- boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG;
- textView.getEditorForTesting().setCursorControlEnabled(true);
- Editor.FLAG_ENABLE_CURSOR_DRAG = true;
-
+ enableFlagsForInsertionHandleGestures();
testInsertionHandle();
testInsertionHandle_multiLine();
-
- textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled);
- Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled;
}
@Test
public void testInsertionHandle_longPressToSelect() {
- // This test only makes sense when Cursor Control flag is enabled.
+ enableFlagsForInsertionHandleGestures();
final TextView textView = mActivity.findViewById(R.id.textview);
- boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled();
- boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG;
- textView.getEditorForTesting().setCursorControlEnabled(true);
- Editor.FLAG_ENABLE_CURSOR_DRAG = true;
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
@@ -546,20 +543,12 @@
onHandleView(com.android.internal.R.id.insertion_handle).perform(longPressHandle(textView));
onView(withId(R.id.textview)).check(hasSelection("world"));
-
- textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled);
- Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled;
}
@Test
public void testInsertionHandle_longPressAndDragToSelect() {
- // This test only makes sense when Cursor Control flag is enabled.
+ enableFlagsForInsertionHandleGestures();
final TextView textView = mActivity.findViewById(R.id.textview);
- boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled();
- boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG;
- textView.getEditorForTesting().setCursorControlEnabled(true);
- Editor.FLAG_ENABLE_CURSOR_DRAG = true;
-
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
@@ -569,19 +558,12 @@
onHandleView(com.android.internal.R.id.insertion_handle)
.perform(longPressAndDragHandle(textView, Handle.INSERTION, text.indexOf('t')));
onView(withId(R.id.textview)).check(hasSelection("the world"));
-
- textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled);
- Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled;
}
@Test
public void testInsertionHandle_doubleTapToSelect() {
- // This test only makes sense when Cursor Control flag is enabled.
+ enableFlagsForInsertionHandleGestures();
final TextView textView = mActivity.findViewById(R.id.textview);
- boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled();
- boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG;
- textView.getEditorForTesting().setCursorControlEnabled(true);
- Editor.FLAG_ENABLE_CURSOR_DRAG = true;
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
@@ -591,19 +573,12 @@
onHandleView(com.android.internal.R.id.insertion_handle).perform(doubleTapHandle(textView));
onView(withId(R.id.textview)).check(hasSelection("world"));
-
- textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled);
- Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled;
}
@Test
public void testInsertionHandle_doubleTapAndDragToSelect() {
- // This test only makes sense when Cursor Control flag is enabled.
+ enableFlagsForInsertionHandleGestures();
final TextView textView = mActivity.findViewById(R.id.textview);
- boolean cursorControlEnabled = textView.getEditorForTesting().getCursorControlEnabled();
- boolean cursorDragEnabled = Editor.FLAG_ENABLE_CURSOR_DRAG;
- textView.getEditorForTesting().setCursorControlEnabled(true);
- Editor.FLAG_ENABLE_CURSOR_DRAG = true;
final String text = "hello the world";
onView(withId(R.id.textview)).perform(replaceText(text));
@@ -614,9 +589,6 @@
onHandleView(com.android.internal.R.id.insertion_handle)
.perform(doubleTapAndDragHandle(textView, Handle.INSERTION, text.indexOf('t')));
onView(withId(R.id.textview)).check(hasSelection("the world"));
-
- textView.getEditorForTesting().setCursorControlEnabled(cursorControlEnabled);
- Editor.FLAG_ENABLE_CURSOR_DRAG = cursorDragEnabled;
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
index 23655a0..fbf75df 100644
--- a/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
+++ b/core/tests/coretests/src/com/android/internal/content/OverlayConfigIterationRule.java
@@ -21,11 +21,11 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
-import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ParsingPackageRead;
import android.os.Build;
import android.util.ArrayMap;
-import com.android.internal.content.om.OverlayConfig.AndroidPackageProvider;
+import com.android.internal.content.om.OverlayConfig.PackageProvider;
import com.android.internal.content.om.OverlayScanner;
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
@@ -39,14 +39,14 @@
import java.io.File;
import java.io.IOException;
import java.util.Map;
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
import java.util.function.Supplier;
/**
* A {@link TestRule} that runs a test case twice. First, the test case runs with a non-null
* {@link OverlayScanner} as if the zygote process is scanning the overlay packages
* and parsing configuration files. The test case then runs with a non-null
- * {@link AndroidPackageProvider} as if the system server is parsing configuration files.
+ * {@link PackageProvider} as if the system server is parsing configuration files.
*
* This simulates what will happen on device. If an exception would be thrown in the zygote, then
* the exception should be thrown in the first run of the test case.
@@ -60,7 +60,7 @@
private final ArrayMap<File, ParsedOverlayInfo> mOverlayStubResults = new ArrayMap<>();
private Supplier<OverlayScanner> mOverlayScanner;
- private AndroidPackageProvider mAndroidPackageProvider;
+ private PackageProvider mPkgProvider;
private Iteration mIteration;
/**
@@ -96,9 +96,9 @@
return mOverlayScanner;
}
- /** Retrieves the {@link AndroidPackageProvider} for the current run of the test. */
- AndroidPackageProvider getPackageProvider() {
- return mAndroidPackageProvider;
+ /** Retrieves the {@link PackageProvider} for the current run of the test. */
+ PackageProvider getPackageProvider() {
+ return mPkgProvider;
}
/** Retrieves the current iteration of the test. */
@@ -123,7 +123,7 @@
}
return scanner;
};
- mAndroidPackageProvider = null;
+ mPkgProvider = null;
mIteration = Iteration.ZYGOTE;
base.evaluate();
@@ -131,14 +131,15 @@
// the system server is parsing the configuration files and using PackageManager to
// retrieving information of overlays.
mOverlayScanner = null;
- mAndroidPackageProvider = Mockito.mock(AndroidPackageProvider.class);
+ mPkgProvider = Mockito.mock(PackageProvider.class);
mIteration = Iteration.SYSTEM_SERVER;
doAnswer((InvocationOnMock invocation) -> {
final Object[] args = invocation.getArguments();
- final Consumer<AndroidPackage> f = (Consumer<AndroidPackage>) args[0];
+ final BiConsumer<ParsingPackageRead, Boolean> f =
+ (BiConsumer<ParsingPackageRead, Boolean>) args[0];
for (Map.Entry<File, ParsedOverlayInfo> overlay :
mOverlayStubResults.entrySet()) {
- final AndroidPackage a = Mockito.mock(AndroidPackage.class);
+ final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
final ParsedOverlayInfo info = overlay.getValue();
when(a.getPackageName()).thenReturn(info.packageName);
when(a.getOverlayTarget()).thenReturn(info.targetPackageName);
@@ -146,12 +147,10 @@
when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
when(a.getOverlayPriority()).thenReturn(info.priority);
when(a.getBaseCodePath()).thenReturn(info.path.getPath());
- when(a.isSystem()).thenReturn(
- !info.path.getPath().contains("data/overlay"));
- f.accept(a);
+ f.accept(a, !info.path.getPath().contains("data/overlay"));
}
return null;
- }).when(mAndroidPackageProvider).forEachPackage(any());
+ }).when(mPkgProvider).forEachPackage(any());
base.evaluate();
}
diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
index cdcf23f..3e40466 100644
--- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
@@ -53,7 +53,10 @@
@Test
public void testDecorContextWithDefaultDisplay() {
- DecorContext context = new DecorContext(mContext.getApplicationContext(), mContext);
+ Display defaultDisplay = new Display(DisplayManagerGlobal.getInstance(), DEFAULT_DISPLAY,
+ new DisplayInfo(), DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+ DecorContext context = new DecorContext(mContext.getApplicationContext(),
+ mContext.createDisplayContext(defaultDisplay));
assertDecorContextDisplay(DEFAULT_DISPLAY, context);
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 08b30f7..78c7b76d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -410,6 +410,7 @@
<privapp-permissions package="com.android.dynsystem">
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
+ <permission name="android.permission.READ_OEM_UNLOCK_STATE"/>
</privapp-permissions>
<privapp-permissions package="com.android.settings">
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index c12159c..5858e39 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -280,7 +280,10 @@
* {@link android.os.Build.VERSION_CODES#Q}, it is no longer required to be
* convex.
*
- * @deprecated The path is no longer required to be convex. Use {@link #setPath} instead.
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#Q}, the restriction
+ * that the path must be convex is removed. However, the API is misnamed until
+ * {@link android.os.Build.VERSION_CODES#R}, when {@link #setPath} is
+ * introduced. Use {@link #setPath} instead.
*/
@Deprecated
public void setConvexPath(@NonNull Path convexPath) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 914c046..56d951c 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -150,11 +150,7 @@
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
SkImageInfo info = uirenderer::BufferDescriptionToImageInfo(bufferDesc, colorSpace);
-
- // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
- const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
- const size_t rowBytes = info.bytesPerPixel() * bufferStride;
- return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
+ return createFrom(hardwareBuffer, info, bufferDesc, palette);
}
sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, SkColorType colorType,
@@ -164,8 +160,14 @@
AHardwareBuffer_describe(hardwareBuffer, &bufferDesc);
SkImageInfo info = SkImageInfo::Make(bufferDesc.width, bufferDesc.height,
colorType, alphaType, colorSpace);
+ return createFrom(hardwareBuffer, info, bufferDesc, palette);
+}
- const size_t rowBytes = info.bytesPerPixel() * bufferDesc.stride;
+sk_sp<Bitmap> Bitmap::createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info,
+ const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette) {
+ // If the stride is 0 we have to use the width as an approximation (eg, compressed buffer)
+ const auto bufferStride = bufferDesc.stride > 0 ? bufferDesc.stride : bufferDesc.width;
+ const size_t rowBytes = info.bytesPerPixel() * bufferStride;
return sk_sp<Bitmap>(new Bitmap(hardwareBuffer, info, rowBytes, palette));
}
#endif
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 3bfb780..b8b5994 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -169,6 +169,12 @@
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
Bitmap(AHardwareBuffer* buffer, const SkImageInfo& info, size_t rowBytes,
BitmapPalette palette);
+
+ // Common code for the two public facing createFrom(AHardwareBuffer*, ...)
+ // methods.
+ // bufferDesc is only used to compute rowBytes.
+ static sk_sp<Bitmap> createFrom(AHardwareBuffer* hardwareBuffer, const SkImageInfo& info,
+ const AHardwareBuffer_Desc& bufferDesc, BitmapPalette palette);
#endif
virtual ~Bitmap();
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index 027a748..e752b55 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -19,5 +19,3 @@
srcs: ["src/**/*.java"],
api_packages: ["com.android.future.usb"],
}
-
-subdirs = ["tests/*"]
diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp
index 63a670c..19ed3d3 100644
--- a/libs/usb/tests/AccessoryChat/Android.bp
+++ b/libs/usb/tests/AccessoryChat/Android.bp
@@ -1,4 +1,3 @@
-subdirs = ["accessorychat"]
//
// Copyright (C) 2011 The Android Open Source Project
//
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 085602c..6006d50 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -27,22 +27,6 @@
public abstract class LocationManagerInternal {
/**
- * Requests that a provider change its allowed state. A provider may or may not honor this
- * request, and if the provider does change its state as a result, that may happen
- * asynchronously after some delay.
- *
- * <p>Setting a provider's state to allowed implies that any consents or terms and conditions
- * that may be necessary to allow the provider are agreed to. Setting a providers state to
- * disallowed implies that any consents or terms and conditions have their agreement revoked.
- *
- * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
- * @param allowed Whether the location provider is being requested to allow or disallow
- * itself
- * @throws IllegalArgumentException if provider is null
- */
- public abstract void requestSetProviderAllowed(@NonNull String provider, boolean allowed);
-
- /**
* Returns true if the given provider is enabled for the given user.
*
* @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index b7817ff..4246c6c 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -37,6 +37,4 @@
@UnsupportedAppUsage
oneway void sendExtraCommand(String command, in Bundle extras);
-
- oneway void requestSetAllowed(boolean allowed);
}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 49fcaab..9cc30d0 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -17,7 +17,6 @@
method @Deprecated protected int onGetStatus(android.os.Bundle);
method @Deprecated protected long onGetStatusUpdateTime();
method protected void onInit();
- method protected void onRequestSetAllowed(boolean);
method protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
method public void reportLocation(android.location.Location);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index bd29d8a..d3fb58f 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -315,17 +315,6 @@
return false;
}
- /**
- * Invoked when the system wishes to request that the provider sets its allowed state as
- * desired. This implies that the caller is providing/retracting consent for any terms and
- * conditions or consents associated with the provider.
- *
- * <p>It is generally only necessary to override this function if the provider has some barriers
- * or gates for enabling/disabling itself, in which case this function should handle those
- * appropriately. A provider that is always allowed has no need to override this function.
- */
- protected void onRequestSetAllowed(boolean allowed) {}
-
private final class Service extends ILocationProvider.Stub {
@Override
@@ -356,10 +345,5 @@
public void sendExtraCommand(String command, Bundle extras) {
onSendExtraCommand(command, extras);
}
-
- @Override
- public void requestSetAllowed(boolean allowed) {
- onRequestSetAllowed(allowed);
- }
}
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 0b825f6..383202b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1654,6 +1654,12 @@
* @hide
* Interface to be notified of changes in the preferred audio device set for a given audio
* strategy.
+ * <p>Note that this listener will only be invoked whenever
+ * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)} or
+ * {@link #removePreferredDeviceForStrategy(AudioProductStrategy)} causes a change in
+ * preferred device. It will not be invoked directly after registration with
+ * {@link #addOnPreferredDeviceForStrategyChangedListener(Executor, OnPreferredDeviceForStrategyChangedListener)}
+ * to indicate which strategies had preferred devices at the time of registration.</p>
* @see #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAttributes)
* @see #removePreferredDeviceForStrategy(AudioProductStrategy)
* @see #getPreferredDeviceForStrategy(AudioProductStrategy)
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index cc35f14..d803311 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -37,7 +37,9 @@
*
* @param schemeUuid The DRM scheme's UUID.
* @return The initialization data for the scheme, or null if the scheme is not supported.
+ * @deprecated Use {@link #getSchemeInitDataCount} and {@link #getSchemeInitDataAt} instead.
*/
+ @Deprecated
public abstract SchemeInitData get(UUID schemeUuid);
/**
diff --git a/media/java/android/media/IMediaRoute2ProviderService.aidl b/media/java/android/media/IMediaRoute2ProviderService.aidl
index cd0def3..6cd2547 100644
--- a/media/java/android/media/IMediaRoute2ProviderService.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderService.aidl
@@ -29,13 +29,13 @@
// MediaRoute2ProviderService#MediaRoute2ProviderServiceStub for readability.
void setCallback(IMediaRoute2ProviderServiceCallback callback);
void updateDiscoveryPreference(in RouteDiscoveryPreference discoveryPreference);
- void setRouteVolume(String routeId, int volume);
+ void setRouteVolume(String routeId, int volume, long requestId);
void requestCreateSession(String packageName, String routeId, long requestId,
in @nullable Bundle sessionHints);
- void selectRoute(String sessionId, String routeId);
- void deselectRoute(String sessionId, String routeId);
- void transferToRoute(String sessionId, String routeId);
- void setSessionVolume(String sessionId, int volume);
- void releaseSession(String sessionId);
+ void selectRoute(String sessionId, String routeId, long requestId);
+ void deselectRoute(String sessionId, String routeId, long requestId);
+ void transferToRoute(String sessionId, String routeId, long requestId);
+ void setSessionVolume(String sessionId, int volume, long requestId);
+ void releaseSession(String sessionId, long requestId);
}
diff --git a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl
index e35b0c4..ab42d75 100644
--- a/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderServiceCallback.aidl
@@ -31,4 +31,5 @@
void notifySessionCreationFailed(long requestId);
void notifySessionUpdated(in RoutingSessionInfo sessionInfo);
void notifySessionReleased(in RoutingSessionInfo sessionInfo);
+ void notifyRequestFailed(long requestId, int reason);
}
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index ffad659..a2f9ee9 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -30,4 +30,5 @@
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
+ void notifyRequestFailed(int requestId, int reason);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index cbec323..c7cb07d 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -71,16 +71,17 @@
void registerManager(IMediaRouter2Manager manager, String packageName);
void unregisterManager(IMediaRouter2Manager manager);
void setRouteVolumeWithManager(IMediaRouter2Manager manager, in MediaRoute2Info route,
- int volume);
+ int volume, int requestId);
void requestCreateSessionWithManager(IMediaRouter2Manager manager, String packageName,
in @nullable MediaRoute2Info route, int requestId);
void selectRouteWithManager(IMediaRouter2Manager manager, String sessionId,
- in MediaRoute2Info route);
+ in MediaRoute2Info route, int requestId);
void deselectRouteWithManager(IMediaRouter2Manager manager, String sessionId,
- in MediaRoute2Info route);
+ in MediaRoute2Info route, int requestId);
void transferToRouteWithManager(IMediaRouter2Manager manager, String sessionId,
- in MediaRoute2Info route);
- void setSessionVolumeWithManager(IMediaRouter2Manager manager, String sessionId, int volume);
- void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId);
+ in MediaRoute2Info route, int requestId);
+ void setSessionVolumeWithManager(IMediaRouter2Manager manager, String sessionId, int volume,
+ int requestId);
+ void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId, int requestId);
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index abc7e0b..f2b4db1 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1959,7 +1959,7 @@
* If this codec is to be used with {@link LinearBlock} and/or {@link
* GraphicBlock}, pass this flag.
* <p>
- * When this flag is set, the following APIs throw IllegalStateException.
+ * When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}.
* <ul>
* <li>{@link #getInputBuffer}
* <li>{@link #getInputImage}
@@ -1986,6 +1986,12 @@
public @interface ConfigureFlag {}
/**
+ * Thrown when the codec is configured for block model and an incompatible API is called.
+ */
+ public class IncompatibleWithBlockModelException extends RuntimeException {
+ }
+
+ /**
* Configures a component.
*
* @param format The format of the input data (decoder) or the desired
@@ -2526,7 +2532,7 @@
throws CryptoException {
synchronized(mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.remove(index);
@@ -2778,7 +2784,7 @@
int flags) throws CryptoException {
synchronized(mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.remove(index);
@@ -2813,7 +2819,7 @@
public final int dequeueInputBuffer(long timeoutUs) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
}
int res = native_dequeueInputBuffer(timeoutUs);
@@ -2848,7 +2854,7 @@
public boolean isMappable() {
synchronized (mLock) {
if (!mValid) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The linear block is invalid");
}
return mMappable;
}
@@ -2867,10 +2873,10 @@
public @NonNull ByteBuffer map() {
synchronized (mLock) {
if (!mValid) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The linear block is invalid");
}
if (!mMappable) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The linear block is not mappable");
}
if (mMapped == null) {
mMapped = native_map();
@@ -2896,7 +2902,7 @@
public void recycle() {
synchronized (mLock) {
if (!mValid) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The linear block is invalid");
}
if (mMapped != null) {
mMapped.setAccessible(false);
@@ -3002,7 +3008,7 @@
public boolean isMappable() {
synchronized (mLock) {
if (!mValid) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The graphic block is invalid");
}
return mMappable;
}
@@ -3021,10 +3027,10 @@
public @NonNull Image map() {
synchronized (mLock) {
if (!mValid) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The graphic block is invalid");
}
if (!mMappable) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The graphic block is not mappable");
}
if (mMapped == null) {
mMapped = native_map();
@@ -3050,7 +3056,7 @@
public void recycle() {
synchronized (mLock) {
if (!mValid) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The graphic block is invalid");
}
if (mMapped != null) {
mMapped.close();
@@ -3127,8 +3133,9 @@
if (buffer == null) {
buffer = new GraphicBlock();
}
- if (width < 0 || height < 0) {
- throw new IllegalArgumentException();
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException(
+ "non-positive width or height: " + width + "x" + height);
}
synchronized (buffer.mLock) {
buffer.native_obtain(width, height, format, usage, codecNames);
@@ -3177,16 +3184,8 @@
* @param block The linear block object
* @param offset The byte offset into the input buffer at which the data starts.
* @param size The number of bytes of valid input data.
- * @param presentationTimeUs The presentation timestamp in microseconds for this
- * buffer. This is normally the media time at which this
- * buffer should be presented (rendered). When using an output
- * surface, this will be propagated as the {@link
- * SurfaceTexture#getTimestamp timestamp} for the frame (after
- * conversion to nanoseconds).
- * @param flags A bitmask of flags
- * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
- * While not prohibited, most codecs do not use the
- * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
+ * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+ * may be null if clear.
* @return this object
* @throws IllegalStateException if a buffer is already set
*/
@@ -3194,59 +3193,17 @@
@NonNull LinearBlock block,
int offset,
int size,
- long presentationTimeUs,
- @BufferFlag int flags) {
+ @Nullable MediaCodec.CryptoInfo cryptoInfo) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
if (mLinearBlock != null || mGraphicBlock != null) {
- throw new IllegalStateException();
+ throw new IllegalStateException("Cannot set block twice");
}
mLinearBlock = block;
mOffset = offset;
mSize = size;
- mPresentationTimeUs = presentationTimeUs;
- mFlags = flags;
- return this;
- }
-
- /**
- * Set an encrypted linear block to this queue request. Exactly one
- * buffer must be set for a queue request before calling {@link #queue}.
- *
- * @param block The linear block object
- * @param offset The byte offset into the input buffer at which the data starts.
- * @param presentationTimeUs The presentation timestamp in microseconds for this
- * buffer. This is normally the media time at which this
- * buffer should be presented (rendered). When using an output
- * surface, this will be propagated as the {@link
- * SurfaceTexture#getTimestamp timestamp} for the frame (after
- * conversion to nanoseconds).
- * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
- * @param flags A bitmask of flags
- * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
- * While not prohibited, most codecs do not use the
- * {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
- * @return this object
- * @throws IllegalStateException if a buffer is already set
- */
- public @NonNull QueueRequest setEncryptedLinearBlock(
- @NonNull LinearBlock block,
- int offset,
- @NonNull MediaCodec.CryptoInfo cryptoInfo,
- long presentationTimeUs,
- @BufferFlag int flags) {
- if (!isAccessible()) {
- throw new IllegalStateException();
- }
- if (mLinearBlock != null || mGraphicBlock != null) {
- throw new IllegalStateException();
- }
- mLinearBlock = block;
- mOffset = offset;
mCryptoInfo = cryptoInfo;
- mPresentationTimeUs = presentationTimeUs;
- mFlags = flags;
return this;
}
@@ -3255,45 +3212,72 @@
* be set for a queue request before calling {@link #queue}.
*
* @param block The graphic block object
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ public @NonNull QueueRequest setGraphicBlock(
+ @NonNull GraphicBlock block) {
+ if (!isAccessible()) {
+ throw new IllegalStateException("The request is stale");
+ }
+ if (mLinearBlock != null || mGraphicBlock != null) {
+ throw new IllegalStateException("Cannot set block twice");
+ }
+ mGraphicBlock = block;
+ return this;
+ }
+
+ /**
+ * Set timestamp to this queue request.
+ *
* @param presentationTimeUs The presentation timestamp in microseconds for this
* buffer. This is normally the media time at which this
* buffer should be presented (rendered). When using an output
* surface, this will be propagated as the {@link
* SurfaceTexture#getTimestamp timestamp} for the frame (after
* conversion to nanoseconds).
+ * @return this object
+ */
+ public @NonNull QueueRequest setPresentationTimeUs(long presentationTimeUs) {
+ if (!isAccessible()) {
+ throw new IllegalStateException("The request is stale");
+ }
+ mPresentationTimeUs = presentationTimeUs;
+ return this;
+ }
+
+ /**
+ * Set flags to this queue request.
+ *
* @param flags A bitmask of flags
* {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}.
* While not prohibited, most codecs do not use the
* {@link #BUFFER_FLAG_KEY_FRAME} flag for input buffers.
* @return this object
- * @throws IllegalStateException if a buffer is already set
*/
- public @NonNull QueueRequest setGraphicBlock(
- @NonNull GraphicBlock block,
- long presentationTimeUs,
- @BufferFlag int flags) {
+ public @NonNull QueueRequest setFlags(@BufferFlag int flags) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
- if (mLinearBlock != null || mGraphicBlock != null) {
- throw new IllegalStateException();
- }
- mGraphicBlock = block;
- mPresentationTimeUs = presentationTimeUs;
mFlags = flags;
return this;
}
/**
- * Add a integer parameter. See {@link MediaFormat} for the list of
- * supported tunings. If there was {@link MediaCodec#setParameters}
+ * Add an integer parameter.
+ * See {@link MediaFormat} for an exhaustive list of supported keys with
+ * values of type int, that can also be set with {@link MediaFormat#setInteger}.
+ *
+ * If there was {@link MediaCodec#setParameters}
* call with the same key which is not processed by the codec yet, the
* value set from this method will override the unprocessed value.
+ *
+ * @return this object
*/
public @NonNull QueueRequest setIntegerParameter(
@NonNull String key, int value) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
mTuningKeys.add(key);
mTuningValues.add(Integer.valueOf(value));
@@ -3301,15 +3285,20 @@
}
/**
- * Add a long parameter. See {@link MediaFormat} for the list of
- * supported tunings. If there was {@link MediaCodec#setParameters}
+ * Add a long parameter.
+ * See {@link MediaFormat} for an exhaustive list of supported keys with
+ * values of type long, that can also be set with {@link MediaFormat#setLong}.
+ *
+ * If there was {@link MediaCodec#setParameters}
* call with the same key which is not processed by the codec yet, the
* value set from this method will override the unprocessed value.
+ *
+ * @return this object
*/
public @NonNull QueueRequest setLongParameter(
@NonNull String key, long value) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
mTuningKeys.add(key);
mTuningValues.add(Long.valueOf(value));
@@ -3317,15 +3306,20 @@
}
/**
- * Add a float parameter. See {@link MediaFormat} for the list of
- * supported tunings. If there was {@link MediaCodec#setParameters}
+ * Add a float parameter.
+ * See {@link MediaFormat} for an exhaustive list of supported keys with
+ * values of type float, that can also be set with {@link MediaFormat#setFloat}.
+ *
+ * If there was {@link MediaCodec#setParameters}
* call with the same key which is not processed by the codec yet, the
* value set from this method will override the unprocessed value.
+ *
+ * @return this object
*/
public @NonNull QueueRequest setFloatParameter(
@NonNull String key, float value) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
mTuningKeys.add(key);
mTuningValues.add(Float.valueOf(value));
@@ -3333,15 +3327,20 @@
}
/**
- * Add a {@link ByteBuffer} parameter. See {@link MediaFormat} for the list of
- * supported tunings. If there was {@link MediaCodec#setParameters}
+ * Add a {@link ByteBuffer} parameter.
+ * See {@link MediaFormat} for an exhaustive list of supported keys with
+ * values of byte buffer, that can also be set with {@link MediaFormat#setByteBuffer}.
+ *
+ * If there was {@link MediaCodec#setParameters}
* call with the same key which is not processed by the codec yet, the
* value set from this method will override the unprocessed value.
+ *
+ * @return this object
*/
public @NonNull QueueRequest setByteBufferParameter(
@NonNull String key, @NonNull ByteBuffer value) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
mTuningKeys.add(key);
mTuningValues.add(value);
@@ -3349,15 +3348,20 @@
}
/**
- * Add a string parameter. See {@link MediaFormat} for the list of
- * supported tunings. If there was {@link MediaCodec#setParameters}
+ * Add a string parameter.
+ * See {@link MediaFormat} for an exhaustive list of supported keys with
+ * values of type string, that can also be set with {@link MediaFormat#setString}.
+ *
+ * If there was {@link MediaCodec#setParameters}
* call with the same key which is not processed by the codec yet, the
* value set from this method will override the unprocessed value.
+ *
+ * @return this object
*/
public @NonNull QueueRequest setStringParameter(
@NonNull String key, @NonNull String value) {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
mTuningKeys.add(key);
mTuningValues.add(value);
@@ -3369,10 +3373,10 @@
*/
public void queue() {
if (!isAccessible()) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The request is stale");
}
if (mLinearBlock == null && mGraphicBlock == null) {
- throw new IllegalStateException();
+ throw new IllegalStateException("No block is set");
}
setAccessible(false);
if (mLinearBlock != null) {
@@ -3447,7 +3451,7 @@
private final ArrayList<QueueRequest> mQueueRequests = new ArrayList<>();
/**
- * Return a clear {@link QueueRequest} object for an input slot index.
+ * Return a {@link QueueRequest} object for an input slot index.
*
* @param index input slot index from
* {@link Callback#onInputBufferAvailable}
@@ -3459,17 +3463,19 @@
public @NonNull QueueRequest getQueueRequest(int index) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_BLOCK) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The codec is not configured for block model");
}
if (index < 0 || index >= mQueueRequests.size()) {
- throw new IllegalArgumentException();
+ throw new IndexOutOfBoundsException("Expected range of index: [0,"
+ + (mQueueRequests.size() - 1) + "]; actual: " + index);
}
QueueRequest request = mQueueRequests.get(index);
if (request == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Unavailable index: " + index);
}
if (!request.isAccessible()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(
+ "The request is stale at index " + index);
}
return request.clear();
}
@@ -3529,7 +3535,7 @@
@NonNull BufferInfo info, long timeoutUs) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
}
int res = native_dequeueOutputBuffer(info, timeoutUs);
@@ -3644,7 +3650,8 @@
frame.clear();
break;
default:
- throw new IllegalStateException();
+ throw new IllegalStateException(
+ "Unrecognized buffer mode: " + mBufferMode);
}
}
releaseOutputBuffer(
@@ -3910,7 +3917,7 @@
public ByteBuffer[] getInputBuffers() {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
if (mCachedInputBuffers == null) {
throw new IllegalStateException();
@@ -3946,7 +3953,7 @@
public ByteBuffer[] getOutputBuffers() {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
if (mCachedOutputBuffers == null) {
throw new IllegalStateException();
@@ -3978,7 +3985,7 @@
public ByteBuffer getInputBuffer(int index) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
}
ByteBuffer newBuffer = getBuffer(true /* input */, index);
@@ -4012,7 +4019,7 @@
public Image getInputImage(int index) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
}
Image newImage = getImage(true /* input */, index);
@@ -4046,7 +4053,7 @@
public ByteBuffer getOutputBuffer(int index) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
}
ByteBuffer newBuffer = getBuffer(false /* input */, index);
@@ -4079,7 +4086,7 @@
public Image getOutputImage(int index) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_LEGACY) {
- throw new IllegalStateException();
+ throw new IncompatibleWithBlockModelException();
}
}
Image newImage = getImage(false /* input */, index);
@@ -4106,7 +4113,7 @@
*/
public @Nullable LinearBlock getLinearBlock() {
if (mGraphicBlock != null) {
- throw new IllegalStateException();
+ throw new IllegalStateException("This output frame is not linear");
}
return mLinearBlock;
}
@@ -4118,7 +4125,7 @@
*/
public @Nullable GraphicBlock getGraphicBlock() {
if (mLinearBlock != null) {
- throw new IllegalStateException();
+ throw new IllegalStateException("This output frame is not graphic");
}
return mGraphicBlock;
}
@@ -4139,7 +4146,7 @@
/**
* Returns a read-only {@link MediaFormat} for this frame. The returned
- * object is valid only while the client is holding the output frame.
+ * object is valid only until the client calls {@link MediaCodec#releaseOutputBuffer}.
*/
public @NonNull MediaFormat getFormat() {
return mFormat;
@@ -4151,7 +4158,7 @@
* Client can find out what the change is by querying {@link MediaFormat}
* object returned from {@link #getFormat}.
*/
- public void getChangedKeys(@NonNull Set<String> keys) {
+ public void retrieveChangedKeys(@NonNull Set<String> keys) {
keys.clear();
keys.addAll(mChangedKeys);
}
@@ -4211,17 +4218,19 @@
public @NonNull OutputFrame getOutputFrame(int index) {
synchronized (mBufferLock) {
if (mBufferMode != BUFFER_MODE_BLOCK) {
- throw new IllegalStateException();
+ throw new IllegalStateException("The codec is not configured for block model");
}
if (index < 0 || index >= mOutputFrames.size()) {
- throw new IllegalArgumentException();
+ throw new IndexOutOfBoundsException("Expected range of index: [0,"
+ + (mQueueRequests.size() - 1) + "]; actual: " + index);
}
OutputFrame frame = mOutputFrames.get(index);
if (frame == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Unavailable index: " + index);
}
if (!frame.isAccessible()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(
+ "The output frame is stale at index " + index);
}
if (!frame.isLoaded()) {
native_getOutputFrame(frame, index);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index bf04fe8..8d63cf04 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -104,10 +104,12 @@
/** @hide */
@IntDef({
- DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_REMOTE_TV,
- DEVICE_TYPE_REMOTE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
+ TYPE_UNKNOWN, TYPE_BUILTIN_SPEAKER, TYPE_WIRED_HEADSET,
+ TYPE_WIRED_HEADPHONES, TYPE_BLUETOOTH_A2DP, TYPE_HDMI, TYPE_USB_DEVICE,
+ TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID,
+ TYPE_REMOTE_TV, TYPE_REMOTE_SPEAKER, TYPE_GROUP})
@Retention(RetentionPolicy.SOURCE)
- public @interface DeviceType {}
+ public @interface Type {}
/**
* The default receiver device type of the route indicating the type is unknown.
@@ -140,6 +142,121 @@
*/
public static final int DEVICE_TYPE_BLUETOOTH = 3;
+
+ /**
+ * The default route type indicating the type is unknown.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_UNKNOWN = 0;
+
+ /**
+ * A route type describing the speaker system (i.e. a mono speaker or stereo speakers) built
+ * in a device.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_BUILTIN_SPEAKER = AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+
+ /**
+ * A route type describing a headset, which is the combination of a headphones and microphone.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_WIRED_HEADSET = AudioDeviceInfo.TYPE_WIRED_HEADSET;
+
+ /**
+ * A route type describing a pair of wired headphones.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_WIRED_HEADPHONES = AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+
+ /**
+ * A route type indicating the presentation of the media is happening
+ * on a bluetooth device such as a bluetooth speaker.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_BLUETOOTH_A2DP = AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
+
+ /**
+ * A route type describing an HDMI connection.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_HDMI = AudioDeviceInfo.TYPE_HDMI;
+
+ /**
+ * A route type describing a USB audio device.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_USB_DEVICE = AudioDeviceInfo.TYPE_USB_DEVICE;
+
+ /**
+ * A route type describing a USB audio device in accessory mode.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_USB_ACCESSORY = AudioDeviceInfo.TYPE_USB_ACCESSORY;
+
+ /**
+ * A route type describing the audio device associated with a dock.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_DOCK = AudioDeviceInfo.TYPE_DOCK;
+
+ /**
+ * A device type describing a USB audio headset.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_USB_HEADSET = AudioDeviceInfo.TYPE_USB_HEADSET;
+
+ /**
+ * A route type describing a Hearing Aid.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
+
+ /**
+ * A route type indicating the presentation of the media is happening on a TV.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_REMOTE_TV = 1001;
+
+ /**
+ * A route type indicating the presentation of the media is happening on a speaker.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_REMOTE_SPEAKER = 1002;
+
+ /**
+ * A route type indicating the presentation of the media is happening on multiple devices.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_GROUP = 2000;
+
/**
* Media feature: Live audio.
* <p>
@@ -196,8 +313,8 @@
final String mId;
final CharSequence mName;
final List<String> mFeatures;
- @DeviceType
- final int mDeviceType;
+ @Type
+ final int mType;
final boolean mIsSystem;
final Uri mIconUri;
final CharSequence mDescription;
@@ -214,7 +331,7 @@
mId = builder.mId;
mName = builder.mName;
mFeatures = builder.mFeatures;
- mDeviceType = builder.mDeviceType;
+ mType = builder.mType;
mIsSystem = builder.mIsSystem;
mIconUri = builder.mIconUri;
mDescription = builder.mDescription;
@@ -231,7 +348,7 @@
mId = in.readString();
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mFeatures = in.createStringArrayList();
- mDeviceType = in.readInt();
+ mType = in.readInt();
mIsSystem = in.readBoolean();
mIconUri = in.readParcelable(null);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
@@ -285,9 +402,26 @@
* {@link #DEVICE_TYPE_REMOTE_TV}, {@link #DEVICE_TYPE_REMOTE_SPEAKER},
* {@link #DEVICE_TYPE_BLUETOOTH}.
*/
- @DeviceType
+ @Type
public int getDeviceType() {
- return mDeviceType;
+ return getType();
+ }
+
+ /**
+ * Gets the type of this route.
+ *
+ * @return The type of this route:
+ * {@link #TYPE_UNKNOWN},
+ * {@link #TYPE_BUILTIN_SPEAKER}, {@link #TYPE_WIRED_HEADSET}, {@link #TYPE_WIRED_HEADPHONES},
+ * {@link #TYPE_BLUETOOTH_A2DP}, {@link #TYPE_HDMI}, {@link #TYPE_DOCK},
+ * {@Link #TYPE_USB_DEVICE}, {@link #TYPE_USB_ACCESSORY}, {@link #TYPE_USB_HEADSET}
+ * {@link #TYPE_HEARING_AID},
+ * {@link #TYPE_REMOTE_TV}, {@link #TYPE_REMOTE_SPEAKER}, {@link #TYPE_GROUP}.
+ * @hide
+ */
+ @Type
+ public int getType() {
+ return mType;
}
/**
@@ -437,7 +571,7 @@
return Objects.equals(mId, other.mId)
&& Objects.equals(mName, other.mName)
&& Objects.equals(mFeatures, other.mFeatures)
- && (mDeviceType == other.mDeviceType)
+ && (mType == other.mType)
&& (mIsSystem == other.mIsSystem)
&& Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mDescription, other.mDescription)
@@ -452,7 +586,7 @@
@Override
public int hashCode() {
// Note: mExtras is not included.
- return Objects.hash(mId, mName, mFeatures, mDeviceType, mIsSystem, mIconUri, mDescription,
+ return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
mProviderId);
}
@@ -488,7 +622,7 @@
dest.writeString(mId);
TextUtils.writeToParcel(mName, dest, flags);
dest.writeStringList(mFeatures);
- dest.writeInt(mDeviceType);
+ dest.writeInt(mType);
dest.writeBoolean(mIsSystem);
dest.writeParcelable(mIconUri, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
@@ -509,8 +643,8 @@
final CharSequence mName;
final List<String> mFeatures;
- @DeviceType
- int mDeviceType = DEVICE_TYPE_UNKNOWN;
+ @Type
+ int mType = TYPE_UNKNOWN;
boolean mIsSystem;
Uri mIconUri;
CharSequence mDescription;
@@ -557,7 +691,7 @@
mId = routeInfo.mId;
mName = routeInfo.mName;
mFeatures = new ArrayList<>(routeInfo.mFeatures);
- mDeviceType = routeInfo.mDeviceType;
+ mType = routeInfo.mType;
mIsSystem = routeInfo.mIsSystem;
mIconUri = routeInfo.mIconUri;
mDescription = routeInfo.mDescription;
@@ -621,8 +755,17 @@
* Sets the route's device type.
*/
@NonNull
- public Builder setDeviceType(@DeviceType int deviceType) {
- mDeviceType = deviceType;
+ public Builder setDeviceType(@Type int type) {
+ return setType(type);
+ }
+
+ /**
+ * Sets the route's type.
+ * @hide
+ */
+ @NonNull
+ public Builder setType(@Type int type) {
+ mType = type;
return this;
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 38233fd..aa0eda1 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.CallSuper;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
@@ -37,6 +38,8 @@
import com.android.internal.annotations.GuardedBy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -51,7 +54,7 @@
* Media apps which use {@link MediaRouter2} can request to play their media on the routes.
* </p><p>
* When {@link MediaRouter2 media router} wants to play media on a route,
- * {@link #onCreateSession(String, String, long, Bundle)} will be called to handle the request.
+ * {@link #onCreateSession(long, String, String, Bundle)} will be called to handle the request.
* A session can be considered as a group of currently selected routes for each connection.
* Create and manage the sessions by yourself, and notify the {@link RoutingSessionInfo
* session infos} when there are any changes.
@@ -61,6 +64,8 @@
* a {@link MediaRouter2 media router} by an application. See
* {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} for the details.
* </p>
+ * Use {@link #notifyRequestFailed(long, int)} to notify the failure with previously received
+ * request ID.
*/
public abstract class MediaRoute2ProviderService extends Service {
private static final String TAG = "MR2ProviderService";
@@ -79,7 +84,53 @@
*
* @see #notifySessionCreated(RoutingSessionInfo, long)
*/
- public static final long REQUEST_ID_UNKNOWN = 0;
+ public static final long REQUEST_ID_NONE = 0;
+
+ /**
+ * The request has failed due to unknown reason.
+ *
+ * @see #notifyRequestFailed(long, int)
+ */
+ public static final int REASON_UNKNOWN_ERROR = 0;
+
+ /**
+ * The request has failed since this service rejected the request.
+ *
+ * @see #notifyRequestFailed(long, int)
+ */
+ public static final int REASON_REJECTED = 1;
+
+ /**
+ * The request has failed due to a network error.
+ *
+ * @see #notifyRequestFailed(long, int)
+ */
+ public static final int REASON_NETWORK_ERROR = 2;
+
+ /**
+ * The request has failed since the requested route is no longer available.
+ *
+ * @see #notifyRequestFailed(long, int)
+ */
+ public static final int REASON_ROUTE_NOT_AVAILABLE = 3;
+
+ /**
+ * The request has failed since the request is not valid. For example, selecting a route
+ * which is not selectable.
+ *
+ * @see #notifyRequestFailed(long, int)
+ */
+ public static final int REASON_INVALID_COMMAND = 4;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = "REASON_", value = {
+ REASON_UNKNOWN_ERROR, REASON_REJECTED, REASON_NETWORK_ERROR, REASON_ROUTE_NOT_AVAILABLE,
+ REASON_INVALID_COMMAND
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Reason {}
private final Handler mHandler;
private final Object mSessionLock = new Object();
@@ -116,20 +167,23 @@
/**
* Called when a volume setting is requested on a route of the provider
*
+ * @param requestId the id of this request
* @param routeId the id of the route
* @param volume the target volume
- * @see MediaRoute2Info#getVolumeMax()
+ * @see MediaRoute2Info.Builder#setVolume(int)
*/
- public abstract void onSetRouteVolume(@NonNull String routeId, int volume);
+ public abstract void onSetRouteVolume(long requestId, @NonNull String routeId, int volume);
/**
* Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on
* a routing session of the provider
*
+ * @param requestId the id of this request
* @param sessionId the id of the routing session
* @param volume the target volume
+ * @see RoutingSessionInfo.Builder#setVolume(int)
*/
- public abstract void onSetSessionVolume(@NonNull String sessionId, int volume);
+ public abstract void onSetSessionVolume(long requestId, @NonNull String sessionId, int volume);
/**
* Gets information of the session with the given id.
@@ -161,14 +215,15 @@
/**
* Notifies clients of that the session is created and ready for use.
* <p>
- * If this session is created without any creation request, use {@link #REQUEST_ID_UNKNOWN}
+ * If this session is created without any creation request, use {@link #REQUEST_ID_NONE}
* as the request ID.
*
* @param sessionInfo information of the new session.
* The {@link RoutingSessionInfo#getId() id} of the session must be unique.
* @param requestId id of the previous request to create this session provided in
- * {@link #onCreateSession(String, String, long, Bundle)}
- * @see #onCreateSession(String, String, long, Bundle)
+ * {@link #onCreateSession(long, String, String, Bundle)}. Can be
+ * {@link #REQUEST_ID_NONE} if this session is created without any request.
+ * @see #onCreateSession(long, String, String, Bundle)
* @see #getSessionInfo(String)
*/
public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
@@ -201,8 +256,8 @@
* Notifies clients of that the session could not be created.
*
* @param requestId id of the previous request to create the session provided in
- * {@link #onCreateSession(String, String, long, Bundle)}.
- * @see #onCreateSession(String, String, long, Bundle)
+ * {@link #onCreateSession(long, String, String, Bundle)}.
+ * @see #onCreateSession(long, String, String, Bundle)
*/
public final void notifySessionCreationFailed(long requestId) {
if (mRemoteCallback == null) {
@@ -246,7 +301,7 @@
* Notifies that the session is released.
*
* @param sessionId id of the released session.
- * @see #onReleaseSession(String)
+ * @see #onReleaseSession(long, String)
*/
public final void notifySessionReleased(@NonNull String sessionId) {
if (TextUtils.isEmpty(sessionId)) {
@@ -273,6 +328,29 @@
}
/**
+ * Notifies to the client that the request has failed.
+ *
+ * @param requestId the ID of the previous request
+ * @param reason the reason why the request has failed
+ *
+ * @see #REASON_UNKNOWN_ERROR
+ * @see #REASON_REJECTED
+ * @see #REASON_NETWORK_ERROR
+ * @see #REASON_ROUTE_NOT_AVAILABLE
+ * @see #REASON_INVALID_COMMAND
+ */
+ public final void notifyRequestFailed(long requestId, @Reason int reason) {
+ if (mRemoteCallback == null) {
+ return;
+ }
+ try {
+ mRemoteCallback.notifyRequestFailed(requestId, reason);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify that the request has failed.");
+ }
+ }
+
+ /**
* Called when the service receives a request to create a session.
* <p>
* You should create and maintain your own session and notifies the client of
@@ -288,9 +366,9 @@
* If you can't create the session or want to reject the request, call
* {@link #notifySessionCreationFailed(long)} with the given {@code requestId}.
*
+ * @param requestId the id of this request
* @param packageName the package name of the application that selected the route
* @param routeId the id of the route initially being connected
- * @param requestId the id of this session creation request
* @param sessionHints an optional bundle of app-specific arguments sent by
* {@link MediaRouter2}, or null if none. The contents of this bundle
* may affect the result of session creation.
@@ -299,8 +377,8 @@
* @see RoutingSessionInfo.Builder#addSelectedRoute(String)
* @see RoutingSessionInfo.Builder#setControlHints(Bundle)
*/
- public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
- long requestId, @Nullable Bundle sessionHints);
+ public abstract void onCreateSession(long requestId, @NonNull String packageName,
+ @NonNull String routeId, @Nullable Bundle sessionHints);
/**
* Called when the session should be released. A client of the session or system can request
@@ -312,44 +390,48 @@
* Note: Calling {@link #notifySessionReleased(String)} will <em>NOT</em> trigger
* this method to be called.
*
+ * @param requestId the id of this request
* @param sessionId id of the session being released.
* @see #notifySessionReleased(String)
* @see #getSessionInfo(String)
*/
- public abstract void onReleaseSession(@NonNull String sessionId);
+ public abstract void onReleaseSession(long requestId, @NonNull String sessionId);
- //TODO: make a way to reject the request
/**
* Called when a client requests selecting a route for the session.
* After the route is selected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
+ * @param requestId the id of this request
* @param sessionId id of the session
* @param routeId id of the route
*/
- public abstract void onSelectRoute(@NonNull String sessionId, @NonNull String routeId);
+ public abstract void onSelectRoute(long requestId, @NonNull String sessionId,
+ @NonNull String routeId);
- //TODO: make a way to reject the request
/**
* Called when a client requests deselecting a route from the session.
* After the route is deselected, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
+ * @param requestId the id of this request
* @param sessionId id of the session
* @param routeId id of the route
*/
- public abstract void onDeselectRoute(@NonNull String sessionId, @NonNull String routeId);
+ public abstract void onDeselectRoute(long requestId, @NonNull String sessionId,
+ @NonNull String routeId);
- //TODO: make a way to reject the request
/**
* Called when a client requests transferring a session to a route.
* After the transfer is finished, call {@link #notifySessionUpdated(RoutingSessionInfo)}
* to update session info.
*
+ * @param requestId the id of this request
* @param sessionId id of the session
* @param routeId id of the route
*/
- public abstract void onTransferToRoute(@NonNull String sessionId, @NonNull String routeId);
+ public abstract void onTransferToRoute(long requestId, @NonNull String sessionId,
+ @NonNull String routeId);
/**
* Called when the {@link RouteDiscoveryPreference discovery preference} has changed.
@@ -439,12 +521,12 @@
}
@Override
- public void setRouteVolume(String routeId, int volume) {
+ public void setRouteVolume(String routeId, int volume, long requestId) {
if (!checkCallerisSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
- MediaRoute2ProviderService.this, routeId, volume));
+ MediaRoute2ProviderService.this, requestId, routeId, volume));
}
@Override
@@ -454,12 +536,12 @@
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
- MediaRoute2ProviderService.this, packageName, routeId, requestId,
+ MediaRoute2ProviderService.this, requestId, packageName, routeId,
requestCreateSession));
}
@Override
- public void selectRoute(@NonNull String sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId, long requestId) {
if (!checkCallerisSystem()) {
return;
}
@@ -468,11 +550,11 @@
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
- MediaRoute2ProviderService.this, sessionId, routeId));
+ MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@Override
- public void deselectRoute(@NonNull String sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId, long requestId) {
if (!checkCallerisSystem()) {
return;
}
@@ -481,11 +563,11 @@
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onDeselectRoute,
- MediaRoute2ProviderService.this, sessionId, routeId));
+ MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@Override
- public void transferToRoute(@NonNull String sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId, long requestId) {
if (!checkCallerisSystem()) {
return;
}
@@ -494,20 +576,20 @@
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onTransferToRoute,
- MediaRoute2ProviderService.this, sessionId, routeId));
+ MediaRoute2ProviderService.this, requestId, sessionId, routeId));
}
@Override
- public void setSessionVolume(String sessionId, int volume) {
+ public void setSessionVolume(String sessionId, int volume, long requestId) {
if (!checkCallerisSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
- MediaRoute2ProviderService.this, sessionId, volume));
+ MediaRoute2ProviderService.this, requestId, sessionId, volume));
}
@Override
- public void releaseSession(@NonNull String sessionId) {
+ public void releaseSession(String sessionId, long requestId) {
if (!checkCallerisSystem()) {
return;
}
@@ -516,7 +598,7 @@
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onReleaseSession,
- MediaRoute2ProviderService.this, sessionId));
+ MediaRoute2ProviderService.this, requestId, sessionId));
}
}
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 28bb4c1..079fee8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -21,6 +21,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
@@ -721,8 +722,7 @@
RoutingController newController) {
for (TransferCallbackRecord record: mTransferCallbackRecords) {
record.mExecutor.execute(
- () -> record.mTransferCallback.onTransferred(oldController,
- newController));
+ () -> record.mTransferCallback.onTransferred(oldController, newController));
}
}
@@ -817,7 +817,7 @@
* @return An optional bundle of app-specific arguments to send to the provider,
* or null if none. The contents of this bundle may affect the result of
* controller creation.
- * @see MediaRoute2ProviderService#onCreateSession(String, String, long, Bundle)
+ * @see MediaRoute2ProviderService#onCreateSession(long, String, String, Bundle)
*/
@Nullable
Bundle onGetControllerHints(@NonNull MediaRoute2Info route);
@@ -866,6 +866,20 @@
}
/**
+ * Gets the original session id set by
+ * {@link RoutingSessionInfo.Builder#Builder(String, String)}.
+ *
+ * @hide
+ */
+ @NonNull
+ @TestApi
+ public String getOriginalId() {
+ synchronized (mControllerLock) {
+ return mSessionInfo.getOriginalId();
+ }
+ }
+
+ /**
* @return the control hints used to control routing session if available.
*/
@Nullable
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 636ee92..ff2c863 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -337,7 +337,8 @@
}
if (client != null) {
try {
- mMediaRouterService.setRouteVolumeWithManager(client, route, volume);
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.setRouteVolumeWithManager(client, route, volume, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to send control request.", ex);
}
@@ -368,8 +369,9 @@
}
if (client != null) {
try {
+ int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.setSessionVolumeWithManager(
- client, sessionInfo.getId(), volume);
+ client, sessionInfo.getId(), volume, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to send control request.", ex);
}
@@ -443,6 +445,12 @@
}
}
+ void notifyRequestFailed(int reason) {
+ for (CallbackRecord record : mCallbackRecords) {
+ record.mExecutor.execute(() -> record.mCallback.onRequestFailed(reason));
+ }
+ }
+
void updatePreferredFeatures(String packageName, List<String> preferredFeatures) {
List<String> prevFeatures = mPreferredFeaturesMap.put(packageName, preferredFeatures);
if ((prevFeatures == null && preferredFeatures.size() == 0)
@@ -593,7 +601,9 @@
}
if (client != null) {
try {
- mMediaRouterService.selectRouteWithManager(mClient, getSessionId(), route);
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.selectRouteWithManager(
+ mClient, getSessionId(), route, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select route for session.", ex);
}
@@ -635,7 +645,9 @@
}
if (client != null) {
try {
- mMediaRouterService.deselectRouteWithManager(mClient, getSessionId(), route);
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.deselectRouteWithManager(
+ mClient, getSessionId(), route, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to remove route from session.", ex);
}
@@ -678,7 +690,9 @@
}
if (client != null) {
try {
- mMediaRouterService.transferToRouteWithManager(mClient, getSessionId(), route);
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.transferToRouteWithManager(
+ mClient, getSessionId(), route, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to transfer to route for session.", ex);
}
@@ -696,7 +710,9 @@
}
if (client != null) {
try {
- mMediaRouterService.releaseSessionWithManager(mClient, getSessionId());
+ int requestId = mNextRequestId.getAndIncrement();
+ mMediaRouterService.releaseSessionWithManager(
+ mClient, getSessionId(), requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to notify of controller release", ex);
}
@@ -783,6 +799,17 @@
public void onPreferredFeaturesChanged(@NonNull String packageName,
@NonNull List<String> preferredFeatures) {}
+ /**
+ * Called when a previous request has failed.
+ *
+ * @param reason the reason that the request has failed. Can be one of followings:
+ * {@link MediaRoute2ProviderService#REASON_UNKNOWN_ERROR},
+ * {@link MediaRoute2ProviderService#REASON_REJECTED},
+ * {@link MediaRoute2ProviderService#REASON_NETWORK_ERROR},
+ * {@link MediaRoute2ProviderService#REASON_ROUTE_NOT_AVAILABLE},
+ * {@link MediaRoute2ProviderService#REASON_INVALID_COMMAND},
+ */
+ public void onRequestFailed(int reason) {}
}
final class CallbackRecord {
@@ -826,6 +853,13 @@
}
@Override
+ public void notifyRequestFailed(int requestId, int reason) {
+ // Note: requestId is not used.
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRequestFailed,
+ MediaRouter2Manager.this, reason));
+ }
+
+ @Override
public void notifyPreferredFeaturesChanged(String packageName, List<String> features) {
mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures,
MediaRouter2Manager.this, packageName, features));
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index d058243..8deb0c4 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -512,15 +512,19 @@
* @return The position of the {@link Uri}, or -1 if it cannot be found.
*/
public int getRingtonePosition(Uri ringtoneUri) {
- if (ringtoneUri == null) return -1;
- final long ringtoneId = ContentUris.parseId(ringtoneUri);
-
- final Cursor cursor = getCursor();
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
- return cursor.getPosition();
+ try {
+ if (ringtoneUri == null) return -1;
+ final long ringtoneId = ContentUris.parseId(ringtoneUri);
+
+ final Cursor cursor = getCursor();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
+ return cursor.getPosition();
+ }
}
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "NumberFormatException while getting ringtone position, returning -1", e);
}
return -1;
}
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 5ecb8f0..2258ee5 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.tv.tuner.V1_0.Constants;
@@ -165,5 +166,33 @@
"Invalid filter types. Main type=" + mainType + ", subtype=" + subtype);
}
+ /**
+ * Gets an throwable instance for the corresponding result.
+ */
+ @Nullable
+ public static void throwExceptionForResult(
+ @TunerConstants.Result int r, @Nullable String msg) {
+ if (msg == null) {
+ msg = "";
+ }
+ switch (r) {
+ case TunerConstants.RESULT_INVALID_ARGUMENT:
+ throw new IllegalArgumentException(msg);
+ case TunerConstants.RESULT_INVALID_STATE:
+ throw new IllegalStateException(msg);
+ case TunerConstants.RESULT_NOT_INITIALIZED:
+ throw new IllegalStateException("Invalid state: not initialized. " + msg);
+ case TunerConstants.RESULT_OUT_OF_MEMORY:
+ throw new OutOfMemoryError(msg);
+ case TunerConstants.RESULT_UNAVAILABLE:
+ throw new IllegalStateException("Invalid state: resource unavailable. " + msg);
+ case TunerConstants.RESULT_UNKNOWN_ERROR:
+ throw new RuntimeException("Unknown error" + msg);
+ default:
+ break;
+ }
+ throw new RuntimeException("Unexpected result " + r + ". " + msg);
+ }
+
private TunerUtils() {}
}
diff --git a/media/java/android/media/tv/tuner/filter/TimeFilter.java b/media/java/android/media/tv/tuner/filter/TimeFilter.java
index a926d59..371ccc4 100644
--- a/media/java/android/media/tv/tuner/filter/TimeFilter.java
+++ b/media/java/android/media/tv/tuner/filter/TimeFilter.java
@@ -17,7 +17,9 @@
package android.media.tv.tuner.filter;
import android.annotation.SystemApi;
+import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.TunerUtils;
/**
* A timer filter is used to filter data based on timestamps.
@@ -51,6 +53,8 @@
private native Long nativeGetSourceTime();
private native int nativeClose();
+ private long mNativeContext;
+
private boolean mEnable = false;
// Called by JNI code
@@ -139,6 +143,9 @@
*/
@Override
public void close() {
- nativeClose();
+ int res = nativeClose();
+ if (res != TunerConstants.RESULT_SUCCESS) {
+ TunerUtils.throwExceptionForResult(res, "Failed to close time filter.");
+ }
}
}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index c17b1b7..47ec7e6 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -160,8 +160,3 @@
"-Wunreachable-code",
],
}
-
-subdirs = [
- "audioeffect",
- "soundpool",
-]
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index f4d2d03..4f31f6c 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -118,10 +118,12 @@
struct fields_t {
jfieldID tunerContext;
jfieldID filterContext;
+ jfieldID timeFilterContext;
jfieldID descramblerContext;
jfieldID dvrContext;
jmethodID frontendInitID;
jmethodID filterInitID;
+ jmethodID timeFilterInitID;
jmethodID dvrInitID;
jmethodID onFrontendEventID;
jmethodID onFilterStatusID;
@@ -237,6 +239,25 @@
return mFilterSp;
}
+/////////////// TimeFilter ///////////////////////
+
+TimeFilter::TimeFilter(sp<ITimeFilter> sp, jobject obj) : mTimeFilterSp(sp) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ mTimeFilterObj = env->NewWeakGlobalRef(obj);
+}
+
+TimeFilter::~TimeFilter() {
+ ALOGD("~TimeFilter");
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteWeakGlobalRef(mTimeFilterObj);
+ mTimeFilterObj = NULL;
+}
+
+sp<ITimeFilter> TimeFilter::getITimeFilter() {
+ return mTimeFilterSp;
+}
+
/////////////// FrontendCallback ///////////////////////
FrontendCallback::FrontendCallback(jweak tunerObj, FrontendId id) : mObject(tunerObj), mId(id) {}
@@ -841,6 +862,36 @@
return filterObj;
}
+jobject JTuner::openTimeFilter() {
+ if (mDemux == NULL) {
+ if (!openDemux()) {
+ return NULL;
+ }
+ }
+ sp<ITimeFilter> iTimeFilterSp;
+ Result res;
+ mDemux->openTimeFilter(
+ [&](Result r, const sp<ITimeFilter>& filter) {
+ iTimeFilterSp = filter;
+ res = r;
+ });
+
+ if (res != Result::SUCCESS || iTimeFilterSp == NULL) {
+ return NULL;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jobject timeFilterObj =
+ env->NewObject(
+ env->FindClass("android/media/tv/tuner/filter/TimeFilter"),
+ gFields.timeFilterInitID);
+ sp<TimeFilter> timeFilterSp = new TimeFilter(iTimeFilterSp, timeFilterObj);
+ timeFilterSp->incStrong(timeFilterObj);
+ env->SetLongField(timeFilterObj, gFields.timeFilterContext, (jlong)timeFilterSp.get());
+
+ return timeFilterObj;
+}
+
jobject JTuner::openDvr(DvrType type, int bufferSize) {
ALOGD("JTuner::openDvr");
if (mDemux == NULL) {
@@ -1420,6 +1471,10 @@
gFields.onFilterStatusID =
env->GetMethodID(filterClazz, "onFilterStatus", "(I)V");
+ jclass timeFilterClazz = env->FindClass("android/media/tv/tuner/filter/TimeFilter");
+ gFields.timeFilterContext = env->GetFieldID(timeFilterClazz, "mNativeContext", "J");
+ gFields.timeFilterInitID = env->GetMethodID(timeFilterClazz, "<init>", "()V");
+
jclass descramblerClazz = env->FindClass("android/media/tv/tuner/Descrambler");
gFields.descramblerContext = env->GetFieldID(descramblerClazz, "mNativeContext", "J");
gFields.descramblerInitID =
@@ -1515,18 +1570,35 @@
static jobject android_media_tv_Tuner_open_filter(
JNIEnv *env, jobject thiz, jint type, jint subType, jlong bufferSize) {
sp<JTuner> tuner = getTuner(env, thiz);
+ DemuxFilterMainType mainType = static_cast<DemuxFilterMainType>(type);
DemuxFilterType filterType {
- .mainType = static_cast<DemuxFilterMainType>(type),
+ .mainType = mainType,
};
- // TODO: other sub types
- filterType.subType.tsFilterType(static_cast<DemuxTsFilterType>(subType));
+ switch(mainType) {
+ case DemuxFilterMainType::TS:
+ filterType.subType.tsFilterType(static_cast<DemuxTsFilterType>(subType));
+ break;
+ case DemuxFilterMainType::MMTP:
+ filterType.subType.mmtpFilterType(static_cast<DemuxMmtpFilterType>(subType));
+ break;
+ case DemuxFilterMainType::IP:
+ filterType.subType.ipFilterType(static_cast<DemuxIpFilterType>(subType));
+ break;
+ case DemuxFilterMainType::TLV:
+ filterType.subType.tlvFilterType(static_cast<DemuxTlvFilterType>(subType));
+ break;
+ case DemuxFilterMainType::ALP:
+ filterType.subType.alpFilterType(static_cast<DemuxAlpFilterType>(subType));
+ break;
+ }
return tuner->openFilter(filterType, bufferSize);
}
-static jobject android_media_tv_Tuner_open_time_filter(JNIEnv, jobject) {
- return NULL;
+static jobject android_media_tv_Tuner_open_time_filter(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ return tuner->openTimeFilter();
}
static DemuxFilterSectionBits getFilterSectionBits(JNIEnv *env, const jobject& settings) {
@@ -1987,26 +2059,98 @@
return 0;
}
-// TODO: implement TimeFilter functions
+static sp<TimeFilter> getTimeFilter(JNIEnv *env, jobject filter) {
+ return (TimeFilter *)env->GetLongField(filter, gFields.timeFilterContext);
+}
+
static int android_media_tv_Tuner_time_filter_set_timestamp(
- JNIEnv, jobject, jlong) {
- return 0;
+ JNIEnv *env, jobject filter, jlong timestamp) {
+ sp<TimeFilter> filterSp = getTimeFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed set timestamp: time filter not found");
+ return (int) Result::INVALID_STATE;
+ }
+ sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
+ Result r = iFilterSp->setTimeStamp(static_cast<uint64_t>(timestamp));
+ return (int) r;
}
-static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv, jobject) {
- return 0;
+static int android_media_tv_Tuner_time_filter_clear_timestamp(JNIEnv *env, jobject filter) {
+ sp<TimeFilter> filterSp = getTimeFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed clear timestamp: time filter not found");
+ return (int) Result::INVALID_STATE;
+ }
+ sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
+ Result r = iFilterSp->clearTimeStamp();
+ return (int) r;
}
-static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv, jobject) {
- return NULL;
+static jobject android_media_tv_Tuner_time_filter_get_timestamp(JNIEnv *env, jobject filter) {
+ sp<TimeFilter> filterSp = getTimeFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed get timestamp: time filter not found");
+ return NULL;
+ }
+
+ sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
+ Result res;
+ uint64_t timestamp;
+ iFilterSp->getTimeStamp(
+ [&](Result r, uint64_t t) {
+ res = r;
+ timestamp = t;
+ });
+ if (res != Result::SUCCESS) {
+ return NULL;
+ }
+
+ jclass longClazz = env->FindClass("java/lang/Long");
+ jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
+
+ jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(timestamp));
+ return longObj;
}
-static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv, jobject) {
- return NULL;
+static jobject android_media_tv_Tuner_time_filter_get_source_time(JNIEnv *env, jobject filter) {
+ sp<TimeFilter> filterSp = getTimeFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed get source time: time filter not found");
+ return NULL;
+ }
+
+ sp<ITimeFilter> iFilterSp = filterSp->getITimeFilter();
+ Result res;
+ uint64_t timestamp;
+ iFilterSp->getSourceTime(
+ [&](Result r, uint64_t t) {
+ res = r;
+ timestamp = t;
+ });
+ if (res != Result::SUCCESS) {
+ return NULL;
+ }
+
+ jclass longClazz = env->FindClass("java/lang/Long");
+ jmethodID longInit = env->GetMethodID(longClazz, "<init>", "(J)V");
+
+ jobject longObj = env->NewObject(longClazz, longInit, static_cast<jlong>(timestamp));
+ return longObj;
}
-static int android_media_tv_Tuner_time_filter_close(JNIEnv, jobject) {
- return 0;
+static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) {
+ sp<TimeFilter> filterSp = getTimeFilter(env, filter);
+ if (filterSp == NULL) {
+ ALOGD("Failed close time filter: time filter not found");
+ return (int) Result::INVALID_STATE;
+ }
+
+ Result r = filterSp->getITimeFilter()->close();
+ if (r == Result::SUCCESS) {
+ filterSp->decStrong(filter);
+ env->SetLongField(filter, gFields.timeFilterContext, 0);
+ }
+ return (int) r;
}
static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index d899bbd..c5590b9 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -54,6 +54,7 @@
using ::android::hardware::tv::tuner::V1_0::IFrontendCallback;
using ::android::hardware::tv::tuner::V1_0::ILnb;
using ::android::hardware::tv::tuner::V1_0::ILnbCallback;
+using ::android::hardware::tv::tuner::V1_0::ITimeFilter;
using ::android::hardware::tv::tuner::V1_0::ITuner;
using ::android::hardware::tv::tuner::V1_0::LnbEventType;
using ::android::hardware::tv::tuner::V1_0::LnbId;
@@ -127,6 +128,14 @@
jweak mFilterObj;
};
+struct TimeFilter : public RefBase {
+ TimeFilter(sp<ITimeFilter> sp, jweak obj);
+ ~TimeFilter();
+ sp<ITimeFilter> getITimeFilter();
+ sp<ITimeFilter> mTimeFilterSp;
+ jweak mTimeFilterObj;
+};
+
struct JTuner : public RefBase {
JTuner(JNIEnv *env, jobject thiz);
sp<ITuner> getTunerService();
@@ -142,6 +151,7 @@
jobject getLnbIds();
jobject openLnbById(int id);
jobject openFilter(DemuxFilterType type, int bufferSize);
+ jobject openTimeFilter();
jobject openDescrambler();
jobject openDvr(DvrType type, int bufferSize);
diff --git a/media/native/Android.bp b/media/native/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/media/native/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index e80562b..bcff6a1 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -19,6 +19,8 @@
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+import static android.media.MediaRoute2ProviderService.REASON_REJECTED;
+import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SAMPLE;
import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.FEATURE_SPECIAL;
@@ -32,6 +34,7 @@
import static com.android.mediaroutertest.SampleMediaRoute2ProviderService.VOLUME_MAX;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -55,7 +58,6 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -69,6 +71,7 @@
@SmallTest
public class MediaRouter2ManagerTest {
private static final String TAG = "MediaRouter2ManagerTest";
+ private static final int WAIT_TIME_MS = 2000;
private static final int TIMEOUT_MS = 5000;
private Context mContext;
@@ -111,6 +114,11 @@
releaseAllSessions();
// unregister callbacks
clearCallbacks();
+
+ SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
+ if (instance != null) {
+ instance.setProxy(null);
+ }
}
@Test
@@ -296,7 +304,7 @@
String selectedSystemRouteId =
MediaRouter2Utils.getOriginalId(
mManager.getActiveSessions().get(0).getSelectedRoutes().get(0));
- Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(Collections.emptyList());
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info volRoute = routes.get(selectedSystemRouteId);
assertNotNull(volRoute);
@@ -398,6 +406,53 @@
}
}
+ /**
+ * Tests that {@link android.media.MediaRoute2ProviderService#notifyRequestFailed(long, int)}
+ * should invoke the callback only when the right requestId is used.
+ */
+ @Test
+ public void testOnRequestFailedCalledForProperRequestId() throws Exception {
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+
+ SampleMediaRoute2ProviderService instance = SampleMediaRoute2ProviderService.getInstance();
+ assertNotNull(instance);
+
+ final List<Long> requestIds = new ArrayList<>();
+ final CountDownLatch onSetRouteVolumeLatch = new CountDownLatch(1);
+ instance.setProxy(new SampleMediaRoute2ProviderService.Proxy() {
+ @Override
+ public void onSetRouteVolume(String routeId, int volume, long requestId) {
+ requestIds.add(requestId);
+ onSetRouteVolumeLatch.countDown();
+ }
+ });
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {});
+ mManager.setRouteVolume(volRoute, 0);
+ assertTrue(onSetRouteVolumeLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertFalse(requestIds.isEmpty());
+
+ final int failureReason = REASON_REJECTED;
+ final CountDownLatch onRequestFailedLatch = new CountDownLatch(1);
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onRequestFailed(int reason) {
+ if (reason == failureReason) {
+ onRequestFailedLatch.countDown();
+ }
+ }
+ });
+
+ final long invalidRequestId = REQUEST_ID_NONE;
+ instance.notifyRequestFailed(invalidRequestId, failureReason);
+ assertFalse(onRequestFailedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ final long validRequestId = requestIds.get(0);
+ instance.notifyRequestFailed(validRequestId, failureReason);
+ assertTrue(onRequestFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
@Test
public void testVolumeHandling() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
@@ -429,10 +484,11 @@
}
@Override
- public void onControlCategoriesChanged(String packageName,
+ public void onPreferredFeaturesChanged(String packageName,
List<String> preferredFeatures) {
if (TextUtils.equals(mPackageName, packageName)
- && preferredFeatures.equals(routeFeatures)) {
+ && preferredFeatures.size() == routeFeatures.size()
+ && preferredFeatures.containsAll(routeFeatures)) {
featuresLatch.countDown();
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 3faefdb..0e7c7fc 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -16,9 +16,9 @@
package com.android.mediaroutertest;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
-import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -75,15 +75,16 @@
@GuardedBy("sLock")
private static SampleMediaRoute2ProviderService sInstance;
+ private Proxy mProxy;
private void initializeRoutes() {
MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
.addFeature(FEATURE_SAMPLE)
- .setDeviceType(DEVICE_TYPE_REMOTE_TV)
+ .setType(TYPE_REMOTE_TV)
.build();
MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2)
.addFeature(FEATURE_SAMPLE)
- .setDeviceType(DEVICE_TYPE_REMOTE_SPEAKER)
+ .setType(TYPE_REMOTE_SPEAKER)
.build();
MediaRoute2Info route3 = new MediaRoute2Info.Builder(
ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
@@ -179,7 +180,13 @@
}
@Override
- public void onSetRouteVolume(String routeId, int volume) {
+ public void onSetRouteVolume(long requestId, String routeId, int volume) {
+ Proxy proxy = mProxy;
+ if (proxy != null) {
+ proxy.onSetRouteVolume(routeId, volume, requestId);
+ return;
+ }
+
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null) {
return;
@@ -192,7 +199,7 @@
}
@Override
- public void onSetSessionVolume(String sessionId, int volume) {
+ public void onSetSessionVolume(long requestId, String sessionId, int volume) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
if (sessionInfo == null) {
return;
@@ -205,7 +212,7 @@
}
@Override
- public void onCreateSession(String packageName, String routeId, long requestId,
+ public void onCreateSession(long requestId, String packageName, String routeId,
@Nullable Bundle sessionHints) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
@@ -238,7 +245,7 @@
}
@Override
- public void onReleaseSession(String sessionId) {
+ public void onReleaseSession(long requestId, String sessionId) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
if (sessionInfo == null) {
return;
@@ -258,7 +265,7 @@
}
@Override
- public void onSelectRoute(String sessionId, String routeId) {
+ public void onSelectRoute(long requestId, String sessionId, String routeId) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null || sessionInfo == null) {
@@ -280,7 +287,7 @@
}
@Override
- public void onDeselectRoute(String sessionId, String routeId) {
+ public void onDeselectRoute(long requestId, String sessionId, String routeId) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
@@ -308,7 +315,7 @@
}
@Override
- public void onTransferToRoute(String sessionId, String routeId) {
+ public void onTransferToRoute(long requestId, String sessionId, String routeId) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
MediaRoute2Info route = mRoutes.get(routeId);
@@ -347,10 +354,18 @@
}
String sessionId = mRouteIdToSessionId.get(routeId);
- onDeselectRoute(sessionId, routeId);
+ onDeselectRoute(REQUEST_ID_NONE, sessionId, routeId);
}
void publishRoutes() {
notifyRoutes(mRoutes.values());
}
+
+ public void setProxy(@Nullable Proxy proxy) {
+ mProxy = proxy;
+ }
+
+ public static class Proxy {
+ public void onSetRouteVolume(String routeId, int volume, long requestId) {}
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index c9ac765..873d7d7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -25,8 +25,11 @@
import android.app.KeyguardManager;
import android.car.Car;
import android.car.media.CarAudioManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Color;
@@ -38,6 +41,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -171,6 +175,17 @@
mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
};
+ private final BroadcastReceiver mHomeButtonPressedBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
+ return;
+ }
+
+ dismissH(Events.DISMISS_REASON_VOLUME_CONTROLLER);
+ }
+ };
+
public CarVolumeDialogImpl(Context context) {
mContext = context;
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
@@ -204,12 +219,18 @@
@Override
public void init(int windowType, Callback callback) {
initDialog();
+
+ mContext.registerReceiverAsUser(mHomeButtonPressedBroadcastReceiver, UserHandle.CURRENT,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* broadcastPermission= */
+ null, /* scheduler= */ null);
}
@Override
public void destroy() {
mHandler.removeCallbacksAndMessages(/* token= */ null);
+ mContext.unregisterReceiver(mHomeButtonPressedBroadcastReceiver);
+
cleanupAudioManager();
}
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index d718eae..b4d520d 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.MANAGE_DYNAMIC_SYSTEM" />
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.READ_OEM_UNLOCK_STATE" />
<application
android:allowBackup="false"
diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml
index 7595d2b..e124be6 100644
--- a/packages/DynamicSystemInstallationService/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values/strings.xml
@@ -18,6 +18,8 @@
<string name="notification_install_inprogress">Install in progress</string>
<!-- Displayed on notification: Dynamic System installation failed [CHAR LIMIT=128] -->
<string name="notification_install_failed">Install failed</string>
+ <!-- Displayed on notification: Image validation failed [CHAR LIMIT=128] -->
+ <string name="notification_image_validation_failed">Image validation failed. Abort installation.</string>
<!-- Displayed on notification: We are running in Dynamic System [CHAR LIMIT=128] -->
<string name="notification_dynsystem_in_use">Currently running a dynamic system. Restart to use the original Android version.</string>
@@ -25,10 +27,11 @@
<string name="notification_action_cancel">Cancel</string>
<!-- Action on notification: Discard installation [CHAR LIMIT=16] -->
<string name="notification_action_discard">Discard</string>
- <!-- Action on notification: Uninstall Dynamic System [CHAR LIMIT=16] -->
- <string name="notification_action_uninstall">Uninstall</string>
<!-- Action on notification: Restart to Dynamic System [CHAR LIMIT=16] -->
<string name="notification_action_reboot_to_dynsystem">Restart</string>
+ <!-- Action on notification: Restart to original Android version [CHAR LIMIT=16] -->
+ <string name="notification_action_reboot_to_origin">Restart</string>
+
<!-- Toast when installed Dynamic System is discarded [CHAR LIMIT=64] -->
<string name="toast_dynsystem_discarded">Discarded dynamic system</string>
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9bae223..37a77be 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -80,6 +80,7 @@
static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
static final String KEY_DSU_SLOT = "KEY_DSU_SLOT";
static final String DEFAULT_DSU_SLOT = "dsu";
+ static final String KEY_PUBKEY = "KEY_PUBKEY";
/*
* Intent actions
@@ -267,6 +268,7 @@
long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
+ String publicKey = intent.getStringExtra(KEY_PUBKEY);
if (TextUtils.isEmpty(dsuSlot)) {
dsuSlot = DEFAULT_DSU_SLOT;
@@ -274,7 +276,7 @@
// TODO: better constructor or builder
mInstallTask =
new InstallationAsyncTask(
- url, dsuSlot, systemSize, userdataSize, this, mDynSystem, this);
+ url, dsuSlot, publicKey, systemSize, userdataSize, this, mDynSystem, this);
mInstallTask.execute();
@@ -408,6 +410,10 @@
}
private Notification buildNotification(int status, int cause) {
+ return buildNotification(status, cause, null);
+ }
+
+ private Notification buildNotification(int status, int cause, Throwable detail) {
Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
.setProgress(0, 0, false);
@@ -456,14 +462,19 @@
.setStyle(new Notification.BigTextStyle().bigText(msgInUse));
builder.addAction(new Notification.Action.Builder(
- null, getString(R.string.notification_action_uninstall),
+ null, getString(R.string.notification_action_reboot_to_origin),
createPendingIntent(ACTION_REBOOT_TO_NORMAL)).build());
break;
case STATUS_NOT_STARTED:
if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
- builder.setContentText(getString(R.string.notification_install_failed));
+ if (detail instanceof InstallationAsyncTask.ImageValidationException) {
+ builder.setContentText(
+ getString(R.string.notification_image_validation_failed));
+ } else {
+ builder.setContentText(getString(R.string.notification_install_failed));
+ }
} else {
// no need to notify the user if the task is not started, or cancelled.
}
@@ -525,7 +536,7 @@
break;
}
- Log.d(TAG, "status=" + statusString + ", cause=" + causeString);
+ Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
boolean notifyOnNotificationBar = true;
@@ -538,7 +549,7 @@
}
if (notifyOnNotificationBar) {
- mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
+ mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
}
for (int i = mClients.size() - 1; i >= 0; i--) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 438c435..f8952ac 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -17,11 +17,13 @@
package com.android.dynsystem;
import android.content.Context;
+import android.gsi.AvbPublicKey;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.image.DynamicSystemManager;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;
import android.webkit.URLUtil;
@@ -51,18 +53,46 @@
private static final List<String> UNSUPPORTED_PARTITIONS =
Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");
- private class UnsupportedUrlException extends RuntimeException {
+ private class UnsupportedUrlException extends Exception {
private UnsupportedUrlException(String message) {
super(message);
}
}
- private class UnsupportedFormatException extends RuntimeException {
+ private class UnsupportedFormatException extends Exception {
private UnsupportedFormatException(String message) {
super(message);
}
}
+ static class ImageValidationException extends Exception {
+ ImageValidationException(String message) {
+ super(message);
+ }
+
+ ImageValidationException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ static class RevocationListFetchException extends ImageValidationException {
+ RevocationListFetchException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ static class KeyRevokedException extends ImageValidationException {
+ KeyRevokedException(String message) {
+ super(message);
+ }
+ }
+
+ static class PublicKeyException extends ImageValidationException {
+ PublicKeyException(String message) {
+ super(message);
+ }
+ }
+
/** UNSET means the installation is not completed */
static final int RESULT_UNSET = 0;
static final int RESULT_OK = 1;
@@ -97,12 +127,14 @@
private final String mUrl;
private final String mDsuSlot;
+ private final String mPublicKey;
private final long mSystemSize;
private final long mUserdataSize;
private final Context mContext;
private final DynamicSystemManager mDynSystem;
private final ProgressListener mListener;
private final boolean mIsNetworkUrl;
+ private final boolean mIsDeviceBootloaderUnlocked;
private DynamicSystemManager.Session mInstallationSession;
private KeyRevocationList mKeyRevocationList;
@@ -115,6 +147,7 @@
InstallationAsyncTask(
String url,
String dsuSlot,
+ String publicKey,
long systemSize,
long userdataSize,
Context context,
@@ -122,12 +155,20 @@
ProgressListener listener) {
mUrl = url;
mDsuSlot = dsuSlot;
+ mPublicKey = publicKey;
mSystemSize = systemSize;
mUserdataSize = userdataSize;
mContext = context;
mDynSystem = dynSystem;
mListener = listener;
mIsNetworkUrl = URLUtil.isNetworkUrl(mUrl);
+ PersistentDataBlockManager pdbManager =
+ (PersistentDataBlockManager)
+ mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+ mIsDeviceBootloaderUnlocked =
+ (pdbManager != null)
+ && (pdbManager.getFlashLockState()
+ == PersistentDataBlockManager.FLASH_LOCK_UNLOCKED);
}
@Override
@@ -157,8 +198,6 @@
return null;
}
- // TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk)
-
mDynSystem.finishInstallation();
} catch (Exception e) {
Log.e(TAG, e.toString(), e);
@@ -242,23 +281,26 @@
String.format(Locale.US, "Unsupported URL: %s", mUrl));
}
- // TODO(yochiang): Bypass this check if device is unlocked
try {
String listUrl = mContext.getString(R.string.key_revocation_list_url);
mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
} catch (IOException | JSONException e) {
- Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List");
mKeyRevocationList = new KeyRevocationList();
- keyRevocationThrowOrWarning(e);
+ imageValidationThrowOrWarning(new RevocationListFetchException(e));
+ }
+ if (mKeyRevocationList.isRevoked(mPublicKey)) {
+ imageValidationThrowOrWarning(new KeyRevokedException(mPublicKey));
}
}
- private void keyRevocationThrowOrWarning(Exception e) throws Exception {
- if (mIsNetworkUrl) {
- throw e;
- } else {
- // If DSU is being installed from a local file URI, then be permissive
+ private void imageValidationThrowOrWarning(ImageValidationException e)
+ throws ImageValidationException {
+ if (mIsDeviceBootloaderUnlocked || !mIsNetworkUrl) {
+ // If device is OEM unlocked or DSU is being installed from a local file URI,
+ // then be permissive.
Log.w(TAG, e.toString());
+ } else {
+ throw e;
}
}
@@ -294,7 +336,8 @@
}
}
- private void installImages() throws IOException, InterruptedException {
+ private void installImages()
+ throws IOException, InterruptedException, ImageValidationException {
if (mStream != null) {
if (mIsZip) {
installStreamingZipUpdate();
@@ -306,12 +349,14 @@
}
}
- private void installStreamingGzUpdate() throws IOException, InterruptedException {
+ private void installStreamingGzUpdate()
+ throws IOException, InterruptedException, ImageValidationException {
Log.d(TAG, "To install a streaming GZ update");
installImage("system", mSystemSize, new GZIPInputStream(mStream), 1);
}
- private void installStreamingZipUpdate() throws IOException, InterruptedException {
+ private void installStreamingZipUpdate()
+ throws IOException, InterruptedException, ImageValidationException {
Log.d(TAG, "To install a streaming ZIP update");
ZipInputStream zis = new ZipInputStream(mStream);
@@ -330,7 +375,8 @@
}
}
- private void installLocalZipUpdate() throws IOException, InterruptedException {
+ private void installLocalZipUpdate()
+ throws IOException, InterruptedException, ImageValidationException {
Log.d(TAG, "To install a local ZIP update");
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -349,8 +395,9 @@
}
}
- private boolean installImageFromAnEntry(ZipEntry entry, InputStream is,
- int numInstalledPartitions) throws IOException, InterruptedException {
+ private boolean installImageFromAnEntry(
+ ZipEntry entry, InputStream is, int numInstalledPartitions)
+ throws IOException, InterruptedException, ImageValidationException {
String name = entry.getName();
Log.d(TAG, "ZipEntry: " + name);
@@ -373,8 +420,9 @@
return true;
}
- private void installImage(String partitionName, long uncompressedSize, InputStream is,
- int numInstalledPartitions) throws IOException, InterruptedException {
+ private void installImage(
+ String partitionName, long uncompressedSize, InputStream is, int numInstalledPartitions)
+ throws IOException, InterruptedException, ImageValidationException {
SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
@@ -445,6 +493,24 @@
publishProgress(progress);
}
}
+
+ AvbPublicKey avbPublicKey = new AvbPublicKey();
+ if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) {
+ imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed"));
+ } else {
+ String publicKey = toHexString(avbPublicKey.sha1);
+ if (mKeyRevocationList.isRevoked(publicKey)) {
+ imageValidationThrowOrWarning(new KeyRevokedException(publicKey));
+ }
+ }
+ }
+
+ private static String toHexString(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
}
private void close() {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 59881e7..d25e3e2 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -886,6 +886,11 @@
<!-- UI debug setting: enable gpu debug layers summary [CHAR LIMIT=50] -->
<string name="enable_gpu_debug_layers_summary">Allow loading GPU debug layers for debug apps</string>
+ <!-- UI debug setting: enable verbose vendor logging [CHAR LIMIT=30] -->
+ <string name="enable_verbose_vendor_logging">Enable verbose vendor logging</string>
+ <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=100] -->
+ <string name="enable_verbose_vendor_logging_summary">Allow additional vendor logs to be included in bug reports, may contain private information</string>
+
<!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
<string name="window_animation_scale_title">Window animation scale</string>
@@ -1260,9 +1265,12 @@
<!-- The notice header of Third-party licenses. not translatable -->
<string name="notice_header" translatable="false"></string>
- <!-- Name of the this device. [CHAR LIMIT=30] -->
- <string name="media_transfer_this_device_name">This device</string>
+ <!-- Name of the phone device. [CHAR LIMIT=30] -->
+ <string name="media_transfer_this_device_name">Phone speaker</string>
<!-- Warning message to tell user is have problem during profile connect, it need to turn off device and back on. [CHAR_LIMIT=NONE] -->
<string name="profile_connect_timeout_subtext">Problem connecting. Turn device off & back on</string>
+
+ <!-- Name of the 3.5mm audio device. [CHAR LIMIT=40] -->
+ <string name="media_transfer_wired_device_name">Wired audio device</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index e0ca1ab..a38091d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -97,7 +97,7 @@
final Resources res = context.getResources();
final DisplayMetrics metrics = new DisplayMetrics();
- context.getDisplay().getRealMetrics(metrics);
+ context.getDisplayNoVerify().getRealMetrics(metrics);
final int currentDensity = metrics.densityDpi;
int currentDensityIndex = -1;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index aad46e9..2dc6f39 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -989,6 +989,11 @@
String value = setting != null ? setting.getValue() : null;
updateGlobalSetting(Settings.Global.ADB_ENABLED,
value, null, true, userId, true);
+
+ setting = getGlobalSetting(Settings.Global.ADB_WIFI_ENABLED);
+ value = setting != null ? setting.getValue() : null;
+ updateGlobalSetting(Settings.Global.ADB_WIFI_ENABLED,
+ value, null, true, userId, true);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4dc372a..0f2ee6a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -111,6 +111,7 @@
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
Settings.Global.ADB_ENABLED,
+ Settings.Global.ADB_WIFI_ENABLED,
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.AIRPLANE_MODE_ON,
Settings.Global.AIRPLANE_MODE_RADIOS,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index de174b1..e066230 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -86,7 +86,9 @@
android_library {
name: "SystemUI-tests",
- manifest: "tests/AndroidManifest.xml",
+ manifest: "tests/AndroidManifest-base.xml",
+ additional_manifests: ["tests/AndroidManifest.xml"],
+
resource_dirs: [
"tests/res",
"res-product",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5458676e..f141578 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -457,6 +457,25 @@
android:excludeFromRecents="true">
</activity>
+ <!-- started from WirelessDebuggingManager -->
+ <activity android:name=".wifi.WifiDebuggingActivity"
+ android:permission="android.permission.MANAGE_DEBUGGING"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+ <activity-alias
+ android:name=".WifiDebuggingActivityAlias"
+ android:permission="android.permission.DUMP"
+ android:targetActivity=".wifi.WifiDebuggingActivity"
+ android:exported="true">
+ </activity-alias>
+ <activity android:name=".wifi.WifiDebuggingSecondaryUserActivity"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
<!-- started from NetworkPolicyManagerService -->
<activity
android:name=".net.NetworkOverLimitActivity"
@@ -696,8 +715,7 @@
<provider
android:name="com.android.keyguard.clock.ClockOptionsProvider"
android:authorities="com.android.keyguard.clock"
- android:enabled="false"
- android:exported="false"
+ android:exported="true"
android:grantUriPermissions="true">
</provider>
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
index 982aa8e..c2dbaca 100644
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -19,39 +19,34 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_section_header_height"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
android:focusable="true"
android:clickable="true"
>
- <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
- android:id="@+id/backgroundNormal"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
- android:id="@+id/backgroundDimmed"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<LinearLayout
android:id="@+id/people_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center"
+ android:layout_marginEnd="8dp"
+ android:gravity="bottom"
android:orientation="horizontal">
- <TextView
+ <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/notification_section_header_padding_left"
- android:gravity="start"
- android:textAlignment="gravity"
- android:text="@string/notification_section_header_conversations"
- android:textSize="12sp"
- android:textColor="@color/notification_section_header_label_color"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- />
+ android:gravity="start|center_vertical"
+ android:layout_weight="1">
+
+ <TextView
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_section_header_conversations"
+ />
+
+ </FrameLayout>
<ImageView
android:layout_width="48dp"
@@ -84,16 +79,10 @@
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginEnd="8dp"
android:padding="8dp"
android:scaleType="fitCenter"
/>
</LinearLayout>
- <com.android.systemui.statusbar.notification.FakeShadowView
- android:id="@+id/fake_shadow"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
</com.android.systemui.statusbar.notification.stack.PeopleHubView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 174a3b8..36ba66a 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -25,9 +25,9 @@
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
- android:layout_height="wrap_content" >
+ android:layout_height="wrap_content">
<com.android.systemui.statusbar.notification.row.FooterViewButton
- style="@android:style/Widget.Material.Button.Borderless"
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/manage_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -35,10 +35,9 @@
android:focusable="true"
android:contentDescription="@string/accessibility_manage_notification"
android:text="@string/manage_notifications_text"
- android:textColor="?attr/wallpaperTextColor"
- android:textAllCaps="false"/>
+ />
<com.android.systemui.statusbar.notification.row.FooterViewButton
- style="@android:style/Widget.Material.Button.Borderless"
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -46,6 +45,6 @@
android:focusable="true"
android:contentDescription="@string/accessibility_clear_all"
android:text="@string/clear_all_notifications_text"
- android:textColor="?attr/wallpaperTextColor"/>
+ />
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
</com.android.systemui.statusbar.notification.row.FooterView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index 508619a..0043d7a 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -19,32 +19,21 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_section_header_height"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
android:focusable="true"
android:clickable="true"
>
- <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
- android:id="@+id/backgroundNormal"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <com.android.systemui.statusbar.notification.row.NotificationBackgroundView
- android:id="@+id/backgroundDimmed"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<include layout="@layout/status_bar_notification_section_header_contents"/>
</LinearLayout>
- <com.android.systemui.statusbar.notification.FakeShadowView
- android:id="@+id/fake_shadow"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
</com.android.systemui.statusbar.notification.stack.SectionHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
index feabd1c..df4b047 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
@@ -16,26 +16,30 @@
<!-- Used by both status_bar_notification_header and SectionHeaderView -->
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
- <TextView
- android:id="@+id/header_label"
+ <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/notification_section_header_padding_left"
- android:gravity="start"
- android:textAlignment="gravity"
- android:text="@string/notification_section_header_gentle"
- android:textSize="12sp"
- android:textColor="@color/notification_section_header_label_color"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- />
+ android:gravity="start|center_vertical"
+ android:layout_weight="1">
+
+ <TextView
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
+ android:id="@+id/header_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/notification_section_header_gentle"
+ />
+
+ </FrameLayout>
<ImageView
android:id="@+id/btn_clear_all"
- android:layout_width="@dimen/notification_section_header_height"
- android:layout_height="@dimen/notification_section_header_height"
- android:layout_marginEnd="4dp"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:src="@drawable/status_bar_notification_section_header_clear_btn"
android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:tintMode="src_in"
+ android:visibility="gone"
/>
</merge>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 15575a4..f9b0666 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -744,7 +744,7 @@
<!-- The top padding of the clear all button -->
<dimen name="clear_all_padding_top">12dp</dimen>
- <dimen name="notification_section_header_height">48dp</dimen>
+ <dimen name="notification_section_header_height">56dp</dimen>
<dimen name="notification_section_header_padding_left">16dp</dimen>
<!-- Largest size an avatar might need to be drawn in the user picker, status bar, or
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5e9feff..4aafec8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -168,6 +168,24 @@
<!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
<string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string>
+ <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
+
+ <!-- Message of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_message">Network Name (SSID)\n<xliff:g id="ssid" example="My wifi">%1$s</xliff:g>\n\nWi\u2011Fi Address (BSSID)\n<xliff:g id="bssid" example="AB:CD:EF:12:34:56">%2$s</xliff:g></string>
+
+ <!-- Option to always allow wireless debugging on this network [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_always">Always allow on this network</string>
+
+ <!-- Button label for confirming acceptance of enabling wireless debugging [CHAR LIMIT=15] -->
+ <string name="wifi_debugging_allow">Allow</string>
+
+ <!-- Title of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_secondary_user_title">Wireless debugging not allowed</string>
+
+ <!-- Message of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_secondary_user_message">The user currently signed in to this device can\u2019t turn on wireless debugging. To use this feature, switch to the primary user.</string>
+
<!-- Title of USB contaminant presence dialog [CHAR LIMIT=NONE] -->
<string name="usb_contaminant_title">USB port disabled</string>
@@ -1204,6 +1222,9 @@
<!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
<string name="notification_section_header_gentle">Silent notifications</string>
+ <!-- Section title for notifications that vibrate or make noise. [CHAR LIMIT=40] -->
+ <string name="notification_section_header_alerting">Alerting notifications</string>
+
<!-- Section title for conversational notifications. [CHAR LIMIT=40] -->
<string name="notification_section_header_conversations">Conversations</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 557e2d6..36c4526 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -554,6 +554,14 @@
<item name="android:gravity">center</item>
</style>
+ <style
+ name="TextAppearance.NotificationSectionHeaderButton"
+ parent="@android:style/Widget.Material.Button.Borderless">
+ <item name="android:textColor">?attr/wallpaperTextColor</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="TextAppearance.HeadsUpStatusBarText"
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
@@ -661,5 +669,5 @@
<item name="android:textSize">12sp</item>
<item name="android:textColor">@color/control_secondary_text</item>
</style>
-
+
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
new file mode 100644
index 0000000..b813e21
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WallpaperManagerCompat.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemui.shared.system;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+
+public class WallpaperManagerCompat {
+ private final WallpaperManager mWallpaperManager;
+
+ public WallpaperManagerCompat(Context context) {
+ mWallpaperManager = context.getSystemService(WallpaperManager.class);
+ }
+
+ public void setWallpaperZoomOut(float zoom) {
+ mWallpaperManager.setWallpaperZoomOut(zoom);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index e475ef1..6f06f69 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -51,7 +51,7 @@
public class KeyguardClockSwitch extends RelativeLayout {
private static final String TAG = "KeyguardClockSwitch";
- private static final boolean CUSTOM_CLOCKS_ENABLED = false;
+ private static final boolean CUSTOM_CLOCKS_ENABLED = true;
/**
* Animation fraction when text is transitioned to/from bold.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 571c4ae..11bf24d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -72,11 +72,12 @@
@Override
public void onDisplayChanged(int displayId) {
if (displayId == DEFAULT_DISPLAY) return;
- final Display display = mDisplayService.getDisplay(displayId);
- if (display != null && mShowing) {
- final Presentation presentation = mPresentations.get(displayId);
- if (presentation != null && !presentation.getDisplay().equals(display)) {
- hidePresentation(displayId);
+ final Presentation presentation = mPresentations.get(displayId);
+ if (presentation != null && mShowing) {
+ hidePresentation(displayId);
+ // update DisplayInfo.
+ final Display display = mDisplayService.getDisplay(displayId);
+ if (display != null) {
showPresentation(display);
}
}
@@ -266,6 +267,11 @@
}
@Override
+ public void cancel() {
+ // Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager.
+ }
+
+ @Override
public void onDetachedFromWindow() {
mClock.removeCallbacks(mMoveTextRunnable);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 09d4d5f..20b1e0d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -57,6 +57,12 @@
int PROMPT_REASON_PREPARE_FOR_UPDATE = 6;
/**
+ * Primary auth is required because the user uses weak/convenience biometrics and hasn't used
+ * primary auth since a while
+ */
+ int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
+
+ /**
* Interface back to keyguard to tell it when security
* @param callback
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 61caf3b..241f96e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -149,7 +149,7 @@
new WindowlessWindowManager(context.getResources().getConfiguration(),
surfaceControl, input);
mUniversalSmartspaceViewHost = new SurfaceControlViewHost(context,
- context.getDisplay(), windowlessWindowManager);
+ context.getDisplayNoVerify(), windowlessWindowManager);
WindowManager.LayoutParams layoutParams =
new WindowManager.LayoutParams(
surfaceControl.getWidth(),
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9ba3860..f57571d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,6 +92,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -108,6 +110,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -264,6 +267,7 @@
// If the user long pressed the lock icon, disabling face auth for the current session.
private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private final Executor mBackgroundExecutor;
/**
* Short delay before restarting biometric authentication after a successful try
@@ -320,12 +324,22 @@
}
};
+ private class BiometricAuthenticated {
+ private final boolean mAuthenticated;
+ private final boolean mIsStrongBiometric;
+
+ BiometricAuthenticated(boolean authenticated, boolean isStrongBiometric) {
+ this.mAuthenticated = authenticated;
+ this.mIsStrongBiometric = isStrongBiometric;
+ }
+ }
+
private SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray();
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray();
- private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
- private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
+ private SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
+ private SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>();
@@ -523,10 +537,11 @@
}
@VisibleForTesting
- protected void onFingerprintAuthenticated(int userId) {
+ protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) {
Assert.isMainThread();
Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
- mUserFingerprintAuthenticated.put(userId, true);
+ mUserFingerprintAuthenticated.put(userId,
+ new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
@@ -536,7 +551,8 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
+ cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT,
+ isStrongBiometric);
}
}
@@ -546,9 +562,21 @@
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
+ // Report unlock with strong or non-strong biometric
+ reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+
Trace.endSection();
}
+ private void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ mBackgroundExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+ }
+ });
+ }
+
private void handleFingerprintAuthFailed() {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -574,7 +602,7 @@
}
}
- private void handleFingerprintAuthenticated(int authUserId) {
+ private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
try {
final int userId;
@@ -592,7 +620,7 @@
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
}
- onFingerprintAuthenticated(userId);
+ onFingerprintAuthenticated(userId, isStrongBiometric);
} finally {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -683,10 +711,11 @@
}
@VisibleForTesting
- protected void onFaceAuthenticated(int userId) {
+ protected void onFaceAuthenticated(int userId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
Assert.isMainThread();
- mUserFaceAuthenticated.put(userId, true);
+ mUserFaceAuthenticated.put(userId,
+ new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
@@ -697,7 +726,8 @@
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricAuthenticated(userId,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE,
+ isStrongBiometric);
}
}
@@ -707,6 +737,9 @@
// Only authenticate face once when assistant is visible
mAssistantVisible = false;
+ // Report unlock with strong or non-strong biometric
+ reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+
Trace.endSection();
}
@@ -737,7 +770,7 @@
}
}
- private void handleFaceAuthenticated(int authUserId) {
+ private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
try {
if (mGoingToSleep) {
@@ -760,7 +793,7 @@
return;
}
if (DEBUG_FACE) Log.d(TAG, "Face auth succeeded for user " + userId);
- onFaceAuthenticated(userId);
+ onFaceAuthenticated(userId, isStrongBiometric);
} finally {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -914,9 +947,13 @@
* Returns whether the user is unlocked with biometrics.
*/
public boolean getUserUnlockedWithBiometric(int userId) {
- boolean fingerprintOrFace = mUserFingerprintAuthenticated.get(userId)
- || mUserFaceAuthenticated.get(userId);
- return fingerprintOrFace && isUnlockingWithBiometricAllowed();
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
+ boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
+ && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
+ boolean faceAllowed = face != null && face.mAuthenticated
+ && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
+ return fingerprintAllowed || faceAllowed;
}
public boolean getUserTrustIsManaged(int userId) {
@@ -970,8 +1007,8 @@
return mUserTrustIsUsuallyManaged.get(userId);
}
- public boolean isUnlockingWithBiometricAllowed() {
- return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
+ public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric);
}
public boolean isUserInLockdown(int userId) {
@@ -1169,7 +1206,7 @@
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
- handleFingerprintAuthenticated(result.getUserId());
+ handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric());
Trace.endSection();
}
@@ -1201,7 +1238,7 @@
@Override
public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
- handleFaceAuthenticated(result.getUserId());
+ handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
Trace.endSection();
}
@@ -1305,9 +1342,9 @@
mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
}
- public boolean isUnlockingWithBiometricAllowed() {
+ public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
int userId = getCurrentUser();
- return isBiometricAllowedForUser(userId);
+ return isBiometricAllowedForUser(isStrongBiometric, userId);
}
public boolean hasUserAuthenticatedSinceBoot() {
@@ -1438,12 +1475,14 @@
Context context,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
- DumpController dumpController) {
+ DumpController dumpController,
+ @Background Executor backgroundExecutor) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
dumpController.registerDumpable(this);
+ mBackgroundExecutor = backgroundExecutor;
mHandler = new Handler(mainLooper) {
@Override
@@ -1753,14 +1792,16 @@
}
private boolean shouldListenForFingerprintAssistant() {
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser());
return mAssistantVisible && mKeyguardOccluded
- && !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
+ && !(fingerprint != null && fingerprint.mAuthenticated)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
private boolean shouldListenForFaceAssistant() {
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
return mAssistantVisible && mKeyguardOccluded
- && !mUserFaceAuthenticated.get(getCurrentUser(), false)
+ && !(face != null && face.mAuthenticated)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
@@ -1817,7 +1858,7 @@
public void onLockIconPressed() {
mLockIconPressed = true;
final int userId = getCurrentUser();
- mUserFaceAuthenticated.put(userId, false);
+ mUserFaceAuthenticated.put(userId, null);
updateFaceListeningState();
mStrongAuthTracker.onStrongAuthRequiredChanged(userId);
}
@@ -2691,9 +2732,12 @@
if (mFpm != null && mFpm.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
- pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
- pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId));
+ pw.println(" allowed="
+ + (fingerprint != null
+ && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric)));
+ pw.println(" auth'd=" + (fingerprint != null && fingerprint.mAuthenticated));
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
@@ -2706,9 +2750,12 @@
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
pw.println(" Face authentication state (user=" + userId + ")");
- pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
- pw.println(" auth'd=" + mUserFaceAuthenticated.get(userId));
+ pw.println(" allowed="
+ + (face != null && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric)));
+ pw.println(" auth'd="
+ + (face != null && face.mAuthenticated));
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFaceDisabled(userId));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 49f72a9..12e0ecd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -239,7 +239,8 @@
* @param userId the user id for which the biometric sample was authenticated
* @param biometricSourceType
*/
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) { }
/**
* Called when biometric authentication provides help string (e.g. "Try again")
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9cd4aec..0367464 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -149,8 +149,6 @@
LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context));
addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor));
- addBuiltinClock(() -> new BubbleClockController(res, layoutInflater, colorExtractor));
- addBuiltinClock(() -> new AnalogClockController(res, layoutInflater, colorExtractor));
// Store the size of the display for generation of clock preview.
DisplayMetrics dm = res.getDisplayMetrics();
@@ -211,7 +209,8 @@
return mContentObserver;
}
- private void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) {
+ @VisibleForTesting
+ void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) {
ClockPlugin plugin = pluginSupplier.get();
mPreviewClocks.addClockPlugin(plugin);
mBuiltinClocks.add(pluginSupplier);
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
index 8503396..85ce313 100644
--- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -57,7 +57,6 @@
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(getStrokePx());
- setLayerType(View.LAYER_TYPE_SOFTWARE, mPaint);
final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
@@ -118,14 +117,8 @@
// Handle color is same as home handle color.
int color = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
mLightColor, mDarkColor);
- // Shadow color is inverse of handle color.
- int shadowColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
- mDarkColor, mLightColor);
if (mPaint.getColor() != color) {
mPaint.setColor(color);
- mPaint.setShadowLayer(/** radius */ getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.assist_handle_shadow_radius), /** shadowDx */ 0,
- /** shadowDy */ 0, /** color */ shadowColor);
if (getVisibility() == VISIBLE && getAlpha() > 0) {
invalidate();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 1a47dac..dc0cb03 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -96,6 +96,7 @@
private void fakeWakeAndUnlock() {
mBiometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
mBiometricUnlockController.onBiometricAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
+ KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT,
+ true /* isStrongBiometric */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index c9104dc..6ce6353 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -96,6 +96,7 @@
private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
private static final boolean VERBOSE = false;
+ private static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS;
private DisplayManager mDisplayManager;
private boolean mIsRegistered;
@@ -454,6 +455,9 @@
private void updateColorInversion(int colorsInvertedValue) {
int tint = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK;
+ if (DEBUG_COLOR) {
+ tint = Color.RED;
+ }
ColorStateList tintList = ColorStateList.valueOf(tint);
if (mOverlays == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index 6a64c83..f719cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -130,7 +130,8 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (userId == KeyguardUpdateMonitor.getCurrentUser()
&& biometricSourceType == BiometricSourceType.FACE) {
mJustUnlockedWithFace = true;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 2f3e336..a084ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -78,7 +78,8 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (userId == KeyguardUpdateMonitor.getCurrentUser()
&& biometricSourceType == BiometricSourceType.FACE) {
mJustUnlockedWithFace = true;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 374153c..2e6c955 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -22,6 +22,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -538,7 +539,8 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (mLockPatternUtils.isSecure(userId)) {
mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt(
userId);
@@ -675,6 +677,9 @@
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+ } else if (any && (strongAuth
+ & STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT;
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
@@ -1842,6 +1847,13 @@
mShowKeyguardWakeLock.release();
}
mKeyguardDisplayManager.show();
+
+ // schedule 4hr idle timeout after which non-strong biometrics (i.e. weak or convenience
+ // biometric) can't be used to unlock device until unlocking with strong biometric or
+ // primary auth (i.e. PIN/pattern/password)
+ mLockPatternUtils.scheduleNonStrongBiometricIdleTimeout(
+ KeyguardUpdateMonitor.getCurrentUser());
+
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index 6c69718..011893d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -244,7 +244,10 @@
ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
mSeamless.setOnClickListener(v -> {
final Intent intent = new Intent()
- .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT);
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+ .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
+ mController.getPackageName())
+ .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, token);
mActivityStarter.startActivity(intent, false, true /* dismissShade */,
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
});
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 84891ec..6663237 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -108,7 +108,7 @@
Log.d(TAG, "Starting countdown");
// Close QS, otherwise the permission dialog appears beneath it
getHost().collapsePanels();
- mController.launchRecordPrompt(this);
+ mController.launchRecordPrompt();
}
private void cancelCountdown() {
@@ -129,6 +129,11 @@
}
@Override
+ public void onCountdownEnd() {
+ refreshState();
+ }
+
+ @Override
public void onRecordingStart() {
refreshState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 6ad9c40..8dad08e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -23,7 +23,6 @@
import android.os.CountDownTimer;
import android.util.Log;
-import com.android.systemui.qs.tiles.ScreenRecordTile;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
@@ -62,7 +61,7 @@
/**
* Show dialog of screen recording options to user.
*/
- public void launchRecordPrompt(ScreenRecordTile tileToUpdate) {
+ public void launchRecordPrompt() {
final ComponentName launcherComponent = new ComponentName(SYSUI_PACKAGE,
SYSUI_SCREENRECORD_LAUNCHER);
final Intent intent = new Intent();
@@ -73,15 +72,17 @@
/**
* Start counting down in preparation to start a recording
- * @param ms Time in ms to count down
+ * @param ms Total time in ms to wait before starting
+ * @param interval Time in ms per countdown step
* @param startIntent Intent to start a recording
* @param stopIntent Intent to stop a recording
*/
- public void startCountdown(long ms, PendingIntent startIntent, PendingIntent stopIntent) {
+ public void startCountdown(long ms, long interval, PendingIntent startIntent,
+ PendingIntent stopIntent) {
mIsStarting = true;
mStopIntent = stopIntent;
- mCountDownTimer = new CountDownTimer(ms, 1000) {
+ mCountDownTimer = new CountDownTimer(ms, interval) {
@Override
public void onTick(long millisUntilFinished) {
for (RecordingStateChangeCallback cb : mListeners) {
@@ -94,7 +95,7 @@
mIsStarting = false;
mIsRecording = true;
for (RecordingStateChangeCallback cb : mListeners) {
- cb.onRecordingEnd();
+ cb.onCountdownEnd();
}
try {
startIntent.send();
@@ -120,7 +121,7 @@
mIsStarting = false;
for (RecordingStateChangeCallback cb : mListeners) {
- cb.onRecordingEnd();
+ cb.onCountdownEnd();
}
}
@@ -144,16 +145,12 @@
* Stop the recording
*/
public void stopRecording() {
- updateState(false);
try {
mStopIntent.send();
+ updateState(false);
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error stopping: " + e.getMessage());
}
-
- for (RecordingStateChangeCallback cb : mListeners) {
- cb.onRecordingEnd();
- }
}
/**
@@ -193,6 +190,12 @@
default void onCountdown(long millisUntilFinished) {}
/**
+ * Called when a countdown to recording has ended. This is a separate method so that if
+ * needed, listeners can handle cases where recording fails to start
+ */
+ default void onCountdownEnd() {}
+
+ /**
* Called when a screen recording has started
*/
default void onRecordingStart() {}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index 566f12b..26973d0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -34,6 +34,7 @@
*/
public class ScreenRecordDialog extends Activity {
private static final long DELAY_MS = 3000;
+ private static final long INTERVAL_MS = 1000;
private final RecordingController mController;
private Switch mAudioSwitch;
@@ -83,6 +84,6 @@
RecordingService.REQUEST_CODE,
RecordingService.getStopIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT);
- mController.startCountdown(DELAY_MS, startIntent, stopIntent);
+ mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9e1e347..f06cd54 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -497,9 +497,9 @@
flashOutAnimator.addUpdateListener(animation ->
mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
- final PointF startPos = new PointF((float) bounds.left, (float) bounds.top);
- final PointF finalPos = new PointF(mScreenshotOffsetXPx,
- mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale);
+ final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
+ final PointF finalPos = new PointF(mScreenshotOffsetXPx + width * cornerScale / 2f,
+ mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale / 2f);
ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
@@ -517,11 +517,13 @@
}
if (t < xPositionPct) {
- mScreenshotView.setX(MathUtils.lerp(
- startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)));
+ float xCenter = MathUtils.lerp(startPos.x, finalPos.x,
+ mFastOutSlowIn.getInterpolation(t / xPositionPct));
+ mScreenshotView.setX(xCenter - width * mScreenshotView.getScaleX() / 2f);
}
- mScreenshotView.setY(MathUtils.lerp(
- startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)));
+ float yCenter = MathUtils.lerp(startPos.y, finalPos.y,
+ mFastOutSlowIn.getInterpolation(t));
+ mScreenshotView.setY(yCenter - height * mScreenshotView.getScaleY() / 2f);
});
toCorner.addListener(new AnimatorListenerAdapter() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 7de70f5..2571521 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -281,7 +281,8 @@
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
- .setAction(Intent.ACTION_SEND),
+ .setAction(Intent.ACTION_SEND)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
@@ -310,7 +311,8 @@
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
mSmartActionsEnabled)
- .setAction(Intent.ACTION_EDIT),
+ .setAction(Intent.ACTION_EDIT)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT, UserHandle.SYSTEM);
Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_edit),
@@ -324,7 +326,8 @@
.putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString())
.putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId)
.putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED,
- mSmartActionsEnabled),
+ mSmartActionsEnabled)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
Icon.createWithResource(r, R.drawable.ic_screenshot_delete),
@@ -361,9 +364,9 @@
String actionType = extras.getString(
ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE);
- Intent intent = new Intent(context,
- GlobalScreenshot.SmartActionsReceiver.class).putExtra(
- GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent);
+ Intent intent = new Intent(context, GlobalScreenshot.SmartActionsReceiver.class)
+ .putExtra(GlobalScreenshot.EXTRA_ACTION_INTENT, action.actionIntent)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled);
PendingIntent broadcastIntent = PendingIntent.getBroadcast(context,
mRandom.nextInt(),
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 2daefbd..56cdff4 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -137,7 +137,8 @@
try {
mLastImeTarget = ActivityTaskManager.getTaskOrganizerController()
.getImeTarget(displayId);
- mShouldAdjustForIme = !mSplitLayout.mDisplayLayout.isLandscape()
+ mShouldAdjustForIme = mLastImeTarget != null
+ && !mSplitLayout.mDisplayLayout.isLandscape()
&& (mLastImeTarget.asBinder()
== mSplits.mSecondary.token.asBinder());
} catch (RemoteException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index ab69d47..4bb8621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -90,7 +90,7 @@
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
- IndentingPrintWriter(pw, " ").use {
+ IndentingPrintWriter(pw, " ").let {
it.println("BlurUtils:")
it.increaseIndent()
it.println("minBlurRadius: $minBlurRadius")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7d3d406..4f8e6cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -645,7 +645,13 @@
@Override
public void onBiometricHelp(int msgId, String helpString,
BiometricSourceType biometricSourceType) {
- if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ // TODO(b/141025588): refactor to reduce repetition of code/comments
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed
+ if (!mKeyguardUpdateMonitor
+ .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) {
return;
}
boolean showSwipeToUnlock =
@@ -705,13 +711,21 @@
private boolean shouldSuppressFingerprintError(int msgId,
KeyguardUpdateMonitor updateMonitor) {
- return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed
+ return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
- return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed
+ return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
|| msgId == FaceManager.FACE_ERROR_CANCELED);
}
@@ -745,8 +759,9 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
- super.onBiometricAuthenticated(userId, biometricSourceType);
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 9c626f7..12add8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -159,6 +159,7 @@
private boolean mDismissed;
private Runnable mOnDismissListener;
private boolean mIncreasedSize;
+ private boolean mTintIcons = true;
public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
this(context, slot, sbn, false);
@@ -612,6 +613,11 @@
}
private void updateIconColor() {
+ if (!mTintIcons) {
+ setColorFilter(null);
+ return;
+ }
+
if (mCurrentSetColor != NO_COLOR) {
if (mMatrixColorFilter == null) {
mMatrix = new float[4 * 5];
@@ -953,6 +959,19 @@
maybeUpdateIconScaleDimens();
}
+ /**
+ * Sets whether the icon should be tinted. If the state differs from the supplied setting, this
+ * will update the icon colors.
+ *
+ * @param shouldTint Whether the icon should be tinted.
+ */
+ public void setTintIcons(boolean shouldTint) {
+ if (mTintIcons != shouldTint) {
+ mTintIcons = shouldTint;
+ updateIconColor();
+ }
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index 0095511..48386dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -22,6 +22,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
import com.android.systemui.util.DeviceConfigProxy
@@ -45,7 +46,7 @@
fun getNotificationBuckets(): IntArray {
return when {
isFilteringEnabled() ->
- intArrayOf(BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
+ intArrayOf(BUCKET_HEADS_UP, BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
NotificationUtils.useNewInterruptionModel(context) ->
intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
else ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index f482d37..25a832d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -29,6 +29,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_NOT_CANCELED;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
@@ -42,6 +44,9 @@
import android.app.Person;
import android.app.RemoteInputHistoryItem;
import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -50,6 +55,7 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
+import android.util.Log;
import android.view.View;
import android.widget.ImageView;
@@ -75,6 +81,8 @@
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -94,11 +102,15 @@
* clean this up in the future.
*/
public final class NotificationEntry extends ListEntry {
+ private static final String TAG = "NotificationEntry";
private final String mKey;
private StatusBarNotification mSbn;
private Ranking mRanking;
+ private StatusBarIcon mSmallIcon;
+ private StatusBarIcon mPeopleAvatar;
+
/*
* Bookkeeping members
*/
@@ -459,12 +471,7 @@
*/
public void createIcons(Context context, StatusBarNotification sbn)
throws InflationException {
- Notification n = sbn.getNotification();
- final Icon smallIcon = n.getSmallIcon();
- if (smallIcon == null) {
- throw new InflationException("No small icon in notification from "
- + sbn.getPackageName());
- }
+ StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
// Construct the icon.
icon = new StatusBarIconView(context,
@@ -482,21 +489,20 @@
aodIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
aodIcon.setIncreasedSize(true);
- final StatusBarIcon ic = new StatusBarIcon(
- sbn.getUser(),
- sbn.getPackageName(),
- smallIcon,
- n.iconLevel,
- n.number,
- StatusBarIconView.contentDescForNotification(context, n));
-
- if (!icon.set(ic) || !expandedIcon.set(ic) || !aodIcon.set(ic)) {
+ try {
+ setIcons(ic, Collections.singletonList(icon));
+ if (isSensitive()) {
+ ic = getIcon(context, sbn, true /* redact */);
+ }
+ setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
+ } catch (InflationException e) {
icon = null;
expandedIcon = null;
centeredIcon = null;
aodIcon = null;
- throw new InflationException("Couldn't create icon: " + ic);
+ throw e;
}
+
expandedIcon.setVisibility(View.INVISIBLE);
expandedIcon.setOnVisibilityChangedListener(
newVisibility -> {
@@ -510,10 +516,130 @@
centeredIcon = new StatusBarIconView(context,
sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
centeredIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- if (!centeredIcon.set(ic)) {
+ try {
+ setIcons(ic, Collections.singletonList(centeredIcon));
+ } catch (InflationException e) {
centeredIcon = null;
- throw new InflationException("Couldn't update centered icon: " + ic);
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Determines if this icon should be tinted based on the sensitivity of the icon, its context
+ * and the user's indicated sensitivity preference.
+ *
+ * @param ic The icon that should/should not be tinted.
+ * @return
+ */
+ private boolean shouldTintIcon(StatusBarIconView ic) {
+ boolean usedInSensitiveContext = (ic == expandedIcon || ic == aodIcon);
+ return !isImportantConversation() || (usedInSensitiveContext && isSensitive());
+ }
+
+
+ private void setIcons(StatusBarIcon ic, List<StatusBarIconView> icons)
+ throws InflationException {
+ for (StatusBarIconView icon: icons) {
+ if (icon == null) {
+ continue;
+ }
+ icon.setTintIcons(shouldTintIcon(icon));
+ if (!icon.set(ic)) {
+ throw new InflationException("Couldn't create icon" + ic);
+ }
+ }
+ }
+
+ private StatusBarIcon getIcon(Context context, StatusBarNotification sbn, boolean redact)
+ throws InflationException {
+ Notification n = sbn.getNotification();
+ final boolean showPeopleAvatar = isImportantConversation() && !redact;
+
+ // If cached, return corresponding cached values
+ if (showPeopleAvatar && mPeopleAvatar != null) {
+ return mPeopleAvatar;
+ } else if (!showPeopleAvatar && mSmallIcon != null) {
+ return mSmallIcon;
+ }
+
+ Icon icon = showPeopleAvatar ? createPeopleAvatar(context) : n.getSmallIcon();
+ if (icon == null) {
+ throw new InflationException("No icon in notification from " + sbn.getPackageName());
+ }
+
+ StatusBarIcon ic = new StatusBarIcon(
+ sbn.getUser(),
+ sbn.getPackageName(),
+ icon,
+ n.iconLevel,
+ n.number,
+ StatusBarIconView.contentDescForNotification(context, n));
+
+ // Cache if important conversation.
+ if (isImportantConversation()) {
+ if (showPeopleAvatar) {
+ mPeopleAvatar = ic;
+ } else {
+ mSmallIcon = ic;
+ }
+ }
+ return ic;
+ }
+
+ private Icon createPeopleAvatar(Context context) throws InflationException {
+ // Attempt to extract form shortcut.
+ String conversationId = getChannel().getConversationId();
+ ShortcutQuery query = new ShortcutQuery()
+ .setPackage(mSbn.getPackageName())
+ .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+ .setShortcutIds(Collections.singletonList(conversationId));
+ List<ShortcutInfo> shortcuts = context.getSystemService(LauncherApps.class)
+ .getShortcuts(query, mSbn.getUser());
+ Icon ic = null;
+ if (shortcuts != null && !shortcuts.isEmpty()) {
+ ic = shortcuts.get(0).getIcon();
+ }
+
+ // Fall back to notification large icon if available
+ if (ic == null) {
+ ic = mSbn.getNotification().getLargeIcon();
+ }
+
+ // Fall back to extract from message
+ if (ic == null) {
+ Bundle extras = mSbn.getNotification().extras;
+ List<Message> messages = Message.getMessagesFromBundleArray(
+ extras.getParcelableArray(Notification.EXTRA_MESSAGES));
+ Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+
+ for (int i = messages.size() - 1; i >= 0; i--) {
+ Message message = messages.get(i);
+ Person sender = message.getSenderPerson();
+ if (sender != null && sender != user) {
+ ic = message.getSenderPerson().getIcon();
+ break;
+ }
+ }
+ }
+
+ // Revert to small icon if still not available
+ if (ic == null) {
+ ic = mSbn.getNotification().getSmallIcon();
+ }
+ if (ic == null) {
+ throw new InflationException("No icon in notification from " + mSbn.getPackageName());
+ }
+ return ic;
+ }
+
+ private void updateSensitiveIconState() {
+ try {
+ StatusBarIcon ic = getIcon(getRow().getContext(), mSbn, isSensitive());
+ setIcons(ic, Arrays.asList(expandedIcon, aodIcon));
+ } catch (InflationException e) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unable to update icon", e);
}
}
}
@@ -544,30 +670,32 @@
throws InflationException {
if (icon != null) {
// Update the icon
- Notification n = sbn.getNotification();
- final StatusBarIcon ic = new StatusBarIcon(
- mSbn.getUser(),
- mSbn.getPackageName(),
- n.getSmallIcon(),
- n.iconLevel,
- n.number,
- StatusBarIconView.contentDescForNotification(context, n));
+ mSmallIcon = null;
+ mPeopleAvatar = null;
+
+ StatusBarIcon ic = getIcon(context, sbn, false /* redact */);
+
icon.setNotification(sbn);
expandedIcon.setNotification(sbn);
aodIcon.setNotification(sbn);
- if (!icon.set(ic) || !expandedIcon.set(ic) || !aodIcon.set(ic)) {
- throw new InflationException("Couldn't update icon: " + ic);
+ setIcons(ic, Arrays.asList(icon, expandedIcon));
+
+ if (isSensitive()) {
+ ic = getIcon(context, sbn, true /* redact */);
}
+ setIcons(ic, Collections.singletonList(aodIcon));
if (centeredIcon != null) {
centeredIcon.setNotification(sbn);
- if (!centeredIcon.set(ic)) {
- throw new InflationException("Couldn't update centered icon: " + ic);
- }
+ setIcons(ic, Collections.singletonList(centeredIcon));
}
}
}
+ private boolean isImportantConversation() {
+ return getChannel() != null && getChannel().isImportantConversation();
+ }
+
public int getContrastedColor(Context context, boolean isLowPriority,
int backgroundColor) {
int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
@@ -996,6 +1124,7 @@
getRow().setSensitive(sensitive, deviceSensitive);
if (sensitive != mSensitive) {
mSensitive = sensitive;
+ updateSensitiveIconState();
if (mOnSensitiveChangedListener != null) {
mOnSensitiveChangedListener.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index e612c07..9c942a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -28,6 +28,7 @@
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
+import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -90,12 +91,12 @@
val bIsHighPriority = b.isHighPriority()
when {
- usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
- usePeopleFiltering && aIsImportantPeople != bIsImportantPeople ->
- if (aIsImportantPeople) -1 else 1
aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
// Provide consistent ranking with headsUpManager
aHeadsUp -> headsUpManager.compare(a, b)
+ usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
+ usePeopleFiltering && aIsImportantPeople != bIsImportantPeople ->
+ if (aIsImportantPeople) -1 else 1
// Upsort current media notification.
aMedia != bMedia -> if (aMedia) -1 else 1
// Upsort PRIORITY_MAX system notifications
@@ -162,7 +163,9 @@
isMedia: Boolean,
isSystemMax: Boolean
) {
- if (usePeopleFiltering && entry.isPeopleNotification()) {
+ if (usePeopleFiltering && isHeadsUp) {
+ entry.bucket = BUCKET_HEADS_UP
+ } else if (usePeopleFiltering && entry.isPeopleNotification()) {
entry.bucket = BUCKET_PEOPLE
} else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority()) {
entry.bucket = BUCKET_ALERTING
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
index efcef71..16574ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -24,8 +24,8 @@
@Binds
abstract fun peopleHubSectionFooterViewAdapter(
- impl: PeopleHubSectionFooterViewAdapterImpl
- ): PeopleHubSectionFooterViewAdapter
+ impl: PeopleHubViewAdapterImpl
+ ): PeopleHubViewAdapter
@Binds
abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
index ec1d6de..e28d03f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -25,17 +25,16 @@
import android.view.View
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager
import javax.inject.Inject
import javax.inject.Singleton
/** Boundary between the View and PeopleHub, as seen by the View. */
-interface PeopleHubSectionFooterViewAdapter {
- fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary)
+interface PeopleHubViewAdapter {
+ fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription
}
-/** Abstract `View` representation of PeopleHub footer in [NotificationSectionsManager]. */
-interface PeopleHubSectionFooterViewBoundary {
+/** Abstract `View` representation of PeopleHub. */
+interface PeopleHubViewBoundary {
/** View used for animating the activity launch caused by clicking a person in the hub. */
val associatedViewForClickAnimation: View
@@ -57,23 +56,22 @@
}
/**
- * Wraps a [PeopleHubSectionFooterViewBoundary] in a [DataListener], and connects it to the data
+ * Wraps a [PeopleHubViewBoundary] in a [DataListener], and connects it to the data
* pipeline.
*
* @param dataSource PeopleHub data pipeline.
*/
@Singleton
-class PeopleHubSectionFooterViewAdapterImpl @Inject constructor(
+class PeopleHubViewAdapterImpl @Inject constructor(
private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
-) : PeopleHubSectionFooterViewAdapter {
+) : PeopleHubViewAdapter {
- override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) {
- dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
- }
+ override fun bindView(viewBoundary: PeopleHubViewBoundary): Subscription =
+ dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
}
private class PeopleHubDataListenerImpl(
- private val viewBoundary: PeopleHubSectionFooterViewBoundary
+ private val viewBoundary: PeopleHubViewBoundary
) : DataListener<PeopleHubViewModelFactory> {
override fun onDataChanged(data: PeopleHubViewModelFactory) {
@@ -92,7 +90,7 @@
* Converts [PeopleHubModel]s into [PeopleHubViewModelFactory]s.
*
* This class serves as the glue between the View layer (which depends on
- * [PeopleHubSectionFooterViewBoundary]) and the Data layer (which produces [PeopleHubModel]s).
+ * [PeopleHubViewBoundary]) and the Data layer (which produces [PeopleHubModel]s).
*/
@Singleton
class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index a0fef00..e79d89f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -67,29 +67,34 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int givenSize = MeasureSpec.getSize(heightMeasureSpec);
+ final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
final int viewHorizontalPadding = getPaddingStart() + getPaddingEnd();
+
+ // Max height is as large as possible, unless otherwise requested
int ownMaxHeight = Integer.MAX_VALUE;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- if (heightMode != MeasureSpec.UNSPECIFIED && givenSize != 0) {
- ownMaxHeight = Math.min(givenSize, ownMaxHeight);
+ if (heightMode != MeasureSpec.UNSPECIFIED && givenHeight != 0) {
+ // Set our max height to what was requested from the parent
+ ownMaxHeight = Math.min(givenHeight, ownMaxHeight);
}
- int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
+
+ // height of the largest child
int maxChildHeight = 0;
+ int atMostOwnMaxHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
- int childHeightSpec = newHeightSpec;
+ int childHeightSpec = atMostOwnMaxHeightSpec;
ViewGroup.LayoutParams layoutParams = child.getLayoutParams();
if (layoutParams.height != ViewGroup.LayoutParams.MATCH_PARENT) {
if (layoutParams.height >= 0) {
- // An actual height is set
- childHeightSpec = layoutParams.height > ownMaxHeight
- ? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
- : MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
+ // If an actual height is set, cap it to the max height
+ childHeightSpec = MeasureSpec.makeMeasureSpec(
+ Math.min(layoutParams.height, ownMaxHeight),
+ MeasureSpec.EXACTLY);
}
child.measure(getChildMeasureSpec(
widthMeasureSpec, viewHorizontalPadding, layoutParams.width),
@@ -100,15 +105,22 @@
mMatchParentViews.add(child);
}
}
+
+ // Set our own height to the given height, or the height of the largest child
int ownHeight = heightMode == MeasureSpec.EXACTLY
- ? givenSize : Math.min(ownMaxHeight, maxChildHeight);
- newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
+ ? givenHeight
+ : Math.min(ownMaxHeight, maxChildHeight);
+ int exactlyOwnHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
+
+ // Now that we know our own height, measure the children that are MATCH_PARENT
for (View child : mMatchParentViews) {
child.measure(getChildMeasureSpec(
widthMeasureSpec, viewHorizontalPadding, child.getLayoutParams().width),
- newHeightSpec);
+ exactlyOwnHeightSpec);
}
mMatchParentViews.clear();
+
+ // Finish up
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, ownHeight);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 23433cb..b3561c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -16,11 +16,10 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
-
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
+import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
@@ -35,18 +34,21 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.people.DataListener;
-import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
-import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewBoundary;
+import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter;
+import com.android.systemui.statusbar.notification.people.PeopleHubViewBoundary;
import com.android.systemui.statusbar.notification.people.PersonViewModel;
+import com.android.systemui.statusbar.notification.people.Subscription;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import javax.inject.Inject;
@@ -63,63 +65,65 @@
private static final String TAG = "NotifSectionsManager";
private static final boolean DEBUG = false;
- private NotificationStackScrollLayout mParent;
private final ActivityStarter mActivityStarter;
private final StatusBarStateController mStatusBarStateController;
private final ConfigurationController mConfigurationController;
- private final int mNumberOfSections;
+ private final PeopleHubViewAdapter mPeopleHubViewAdapter;
private final NotificationSectionsFeatureManager mSectionsFeatureManager;
- private final NotificationRowComponent.Builder mNotificationRowComponentBuilder;
+ private final int mNumberOfSections;
+
+ private final PeopleHubViewBoundary mPeopleHubViewBoundary = new PeopleHubViewBoundary() {
+ @Override
+ public void setVisible(boolean isVisible) {
+ if (mPeopleHubVisible != isVisible) {
+ mPeopleHubVisible = isVisible;
+ if (mInitialized) {
+ updateSectionBoundaries();
+ }
+ }
+ }
+
+ @NonNull
+ @Override
+ public View getAssociatedViewForClickAnimation() {
+ return mPeopleHubView;
+ }
+
+ @NonNull
+ @Override
+ public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() {
+ return mPeopleHubView.getPersonViewAdapters();
+ }
+ };
+
+ private NotificationStackScrollLayout mParent;
private boolean mInitialized = false;
private SectionHeaderView mGentleHeader;
- private boolean mGentleHeaderVisible = false;
-
- private boolean mPeopleHubVisible = false;
- private PeopleHubView mPeopleHubView;
- private final PeopleHubSectionFooterViewAdapter mPeopleHubViewAdapter;
- private final PeopleHubSectionFooterViewBoundary mPeopleHubViewBoundary =
- new PeopleHubSectionFooterViewBoundary() {
- @Override
- public void setVisible(boolean isVisible) {
- if (mPeopleHubVisible != isVisible) {
- mPeopleHubVisible = isVisible;
- if (mInitialized) {
- updateSectionBoundaries();
- }
- }
- }
-
- @NonNull
- @Override
- public View getAssociatedViewForClickAnimation() {
- return mPeopleHubView;
- }
-
- @NonNull
- @Override
- public Sequence<DataListener<PersonViewModel>> getPersonViewAdapters() {
- return mPeopleHubView.getPersonViewAdapters();
- }
- };
-
+ private boolean mGentleHeaderVisible;
@Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
+ private SectionHeaderView mAlertingHeader;
+ private boolean mAlertingHeaderVisible;
+
+ private PeopleHubView mPeopleHubView;
+ private boolean mPeopleHeaderVisible;
+ private boolean mPeopleHubVisible = false;
+ @Nullable private Subscription mPeopleHubSubscription;
+
@Inject
NotificationSectionsManager(
ActivityStarter activityStarter,
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
- PeopleHubSectionFooterViewAdapter peopleHubViewAdapter,
- NotificationSectionsFeatureManager sectionsFeatureManager,
- NotificationRowComponent.Builder notificationRowComponentBuilder) {
+ PeopleHubViewAdapter peopleHubViewAdapter,
+ NotificationSectionsFeatureManager sectionsFeatureManager) {
mActivityStarter = activityStarter;
mStatusBarStateController = statusBarStateController;
mConfigurationController = configurationController;
mPeopleHubViewAdapter = peopleHubViewAdapter;
mSectionsFeatureManager = sectionsFeatureManager;
mNumberOfSections = mSectionsFeatureManager.getNumberOfBuckets();
- mNotificationRowComponentBuilder = notificationRowComponentBuilder;
}
NotificationSection[] createSectionsForBuckets() {
@@ -141,105 +145,81 @@
mInitialized = true;
mParent = parent;
reinflateViews(layoutInflater);
- mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
mConfigurationController.addCallback(mConfigurationListener);
}
+ private <T extends ExpandableView> T reinflateView(
+ T view, LayoutInflater layoutInflater, @LayoutRes int layoutResId) {
+ int oldPos = -1;
+ if (view != null) {
+ if (view.getTransientContainer() != null) {
+ view.getTransientContainer().removeView(mGentleHeader);
+ } else if (view.getParent() != null) {
+ oldPos = mParent.indexOfChild(view);
+ mParent.removeView(view);
+ }
+ }
+
+ view = (T) layoutInflater.inflate(layoutResId, mParent, false);
+
+ if (oldPos != -1) {
+ mParent.addView(view, oldPos);
+ }
+
+ return view;
+ }
+
/**
* Reinflates the entire notification header, including all decoration views.
*/
void reinflateViews(LayoutInflater layoutInflater) {
- int oldGentleHeaderPos = -1;
- int oldPeopleHubPos = -1;
- if (mGentleHeader != null) {
- if (mGentleHeader.getTransientContainer() != null) {
- mGentleHeader.getTransientContainer().removeView(mGentleHeader);
- } else if (mGentleHeader.getParent() != null) {
- oldGentleHeaderPos = mParent.indexOfChild(mGentleHeader);
- mParent.removeView(mGentleHeader);
- }
- }
- if (mPeopleHubView != null) {
- if (mPeopleHubView.getTransientContainer() != null) {
- mPeopleHubView.getTransientContainer().removeView(mPeopleHubView);
- } else if (mPeopleHubView.getParent() != null) {
- oldPeopleHubPos = mParent.indexOfChild(mPeopleHubView);
- mParent.removeView(mPeopleHubView);
- }
- }
-
- mGentleHeader = (SectionHeaderView) layoutInflater.inflate(
- R.layout.status_bar_notification_section_header, mParent, false);
- NotificationRowComponent sectionHeaderComponent = mNotificationRowComponentBuilder
- .activatableNotificationView(mGentleHeader)
- .build();
- sectionHeaderComponent.getActivatableNotificationViewController().init();
-
+ mGentleHeader = reinflateView(
+ mGentleHeader, layoutInflater, R.layout.status_bar_notification_section_header);
+ mGentleHeader.setHeaderText(R.string.notification_section_header_gentle);
mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick);
- if (oldGentleHeaderPos != -1) {
- mParent.addView(mGentleHeader, oldGentleHeaderPos);
+ mAlertingHeader = reinflateView(
+ mAlertingHeader, layoutInflater, R.layout.status_bar_notification_section_header);
+ mAlertingHeader.setHeaderText(R.string.notification_section_header_alerting);
+ mAlertingHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
+
+ if (mPeopleHubSubscription != null) {
+ mPeopleHubSubscription.unsubscribe();
}
-
- mPeopleHubView = (PeopleHubView) layoutInflater.inflate(
- R.layout.people_strip, mParent, false);
-
- NotificationRowComponent notificationRowComponent = mNotificationRowComponentBuilder
- .activatableNotificationView(mPeopleHubView)
- .build();
- notificationRowComponent.getActivatableNotificationViewController().init();
-
- if (oldPeopleHubPos != -1) {
- mParent.addView(mPeopleHubView, oldPeopleHubPos);
- }
+ mPeopleHubView = reinflateView(mPeopleHubView, layoutInflater, R.layout.people_strip);
+ mPeopleHubSubscription = mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
}
- /** Listener for when the "clear all" buttton is clciked on the gentle notification header. */
+ /** Listener for when the "clear all" button is clicked on the gentle notification header. */
void setOnClearGentleNotifsClickListener(View.OnClickListener listener) {
mOnClearGentleNotifsClickListener = listener;
}
- /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */
- void onUiModeChanged() {
- mGentleHeader.onUiModeChanged();
- }
-
@Override
public boolean beginsSection(@NonNull View view, @Nullable View previous) {
- boolean begin = false;
- if (view instanceof ActivatableNotificationView) {
- if (previous instanceof ActivatableNotificationView) {
- // If we're drawing the first non-person notification, break out a section
- ActivatableNotificationView curr = (ActivatableNotificationView) view;
- ActivatableNotificationView prev = (ActivatableNotificationView) previous;
-
- begin = getBucket(curr) != getBucket(prev);
- }
- }
-
- if (!begin) {
- begin = view == mGentleHeader || view == mPeopleHubView;
- }
-
- return begin;
+ return view == mGentleHeader
+ || view == mPeopleHubView
+ || view == mAlertingHeader
+ || !Objects.equals(getBucket(view), getBucket(previous));
}
private boolean isUsingMultipleSections() {
return mNumberOfSections > 1;
}
- private @PriorityBucket int getBucket(ActivatableNotificationView view)
- throws IllegalArgumentException {
- if (view instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) view).getEntry().getBucket();
- } else if (view == mGentleHeader) {
+ @Nullable
+ private Integer getBucket(View view) {
+ if (view == mGentleHeader) {
return BUCKET_SILENT;
} else if (view == mPeopleHubView) {
return BUCKET_PEOPLE;
+ } else if (view == mAlertingHeader) {
+ return BUCKET_ALERTING;
+ } else if (view instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) view).getEntry().getBucket();
}
-
- throw new IllegalArgumentException("I don't know how to find a bucket for this view :(");
+ return null;
}
/**
@@ -251,118 +231,104 @@
return;
}
- boolean peopleNotificationsPresent = false;
- int firstNonHeadsUpIndex = -1;
- int firstGentleIndex = -1;
- int notifCount = 0;
+ final boolean showHeaders = mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
+ final boolean usingPeopleFiltering = mSectionsFeatureManager.isFilteringEnabled();
- final int n = mParent.getChildCount();
- for (int i = 0; i < n; i++) {
- View child = mParent.getChildAt(i);
- if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
- notifCount++;
+ boolean peopleNotifsPresent = false;
+ int peopleHeaderTarget = -1;
+ int alertingHeaderTarget = -1;
+ int gentleHeaderTarget = -1;
+
+ int viewCount = 0;
+
+ if (showHeaders) {
+ final int childCount = mParent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mParent.getChildAt(i);
+ if (child.getVisibility() == View.GONE
+ || !(child instanceof ExpandableNotificationRow)) {
+ continue;
+ }
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- if (firstNonHeadsUpIndex == -1 && !row.isHeadsUp()) {
- firstNonHeadsUpIndex = i;
+ switch (row.getEntry().getBucket()) {
+ case BUCKET_PEOPLE:
+ if (peopleHeaderTarget == -1) {
+ peopleNotifsPresent = true;
+ peopleHeaderTarget = viewCount;
+ viewCount++;
+ }
+ break;
+ case BUCKET_ALERTING:
+ if (usingPeopleFiltering && alertingHeaderTarget == -1) {
+ alertingHeaderTarget = viewCount;
+ viewCount++;
+ }
+ break;
+ case BUCKET_SILENT:
+ if (gentleHeaderTarget == -1) {
+ gentleHeaderTarget = viewCount;
+ viewCount++;
+ }
+ break;
}
- if (row.getEntry().getBucket() == BUCKET_PEOPLE) {
- peopleNotificationsPresent = true;
- }
- if (row.getEntry().getBucket() == BUCKET_SILENT) {
- firstGentleIndex = i;
- break;
+ viewCount++;
+ }
+ if (usingPeopleFiltering && mPeopleHubVisible && peopleHeaderTarget == -1) {
+ // Insert the people header even if there are no people visible, in order to show
+ // the hub. Put it directly above the next header.
+ if (alertingHeaderTarget != -1) {
+ peopleHeaderTarget = alertingHeaderTarget;
+ alertingHeaderTarget++;
+ gentleHeaderTarget++;
+ } else if (gentleHeaderTarget != -1) {
+ peopleHeaderTarget = gentleHeaderTarget;
+ gentleHeaderTarget++;
+ } else {
+ // Put it at the end of the list.
+ peopleHeaderTarget = viewCount;
}
}
}
- if (firstNonHeadsUpIndex == -1) {
- firstNonHeadsUpIndex = firstGentleIndex != -1 ? firstGentleIndex : notifCount;
- }
+ // Allow swiping the people header if the section is empty
+ mPeopleHubView.setCanSwipe(mPeopleHubVisible && !peopleNotifsPresent);
- // make room for peopleHub
- int offset = adjustPeopleHubVisibilityAndPosition(
- firstNonHeadsUpIndex, peopleNotificationsPresent);
- if (firstGentleIndex != -1) {
- firstGentleIndex += offset;
- }
-
- adjustGentleHeaderVisibilityAndPosition(firstGentleIndex);
-
- mGentleHeader.setAreThereDismissableGentleNotifs(
- mParent.hasActiveClearableNotifications(ROWS_GENTLE));
+ mPeopleHeaderVisible = adjustHeaderVisibilityAndPosition(
+ peopleHeaderTarget, mPeopleHubView, mPeopleHeaderVisible);
+ mAlertingHeaderVisible = adjustHeaderVisibilityAndPosition(
+ alertingHeaderTarget, mAlertingHeader, mAlertingHeaderVisible);
+ mGentleHeaderVisible = adjustHeaderVisibilityAndPosition(
+ gentleHeaderTarget, mGentleHeader, mGentleHeaderVisible);
}
- private void adjustGentleHeaderVisibilityAndPosition(int firstGentleNotifIndex) {
- final boolean showGentleHeader =
- firstGentleNotifIndex != -1
- && mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
- final int currentHeaderIndex = mParent.indexOfChild(mGentleHeader);
-
- if (!showGentleHeader) {
- if (mGentleHeaderVisible) {
- mGentleHeaderVisible = false;
- mParent.removeView(mGentleHeader);
+ private boolean adjustHeaderVisibilityAndPosition(
+ int targetIndex, StackScrollerDecorView header, boolean isCurrentlyVisible) {
+ if (targetIndex == -1) {
+ if (isCurrentlyVisible) {
+ mParent.removeView(header);
}
+ return false;
} else {
- if (!mGentleHeaderVisible) {
- mGentleHeaderVisible = true;
+ if (header instanceof SwipeableView) {
+ ((SwipeableView) header).resetTranslation();
+ }
+ if (!isCurrentlyVisible) {
// If the header is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will happen
// automatically when the view's intro animation starts, but it's a fragile link.
- if (mGentleHeader.getTransientContainer() != null) {
- mGentleHeader.getTransientContainer().removeTransientView(mGentleHeader);
- mGentleHeader.setTransientContainer(null);
+ if (header.getTransientContainer() != null) {
+ header.getTransientContainer().removeTransientView(header);
+ header.setTransientContainer(null);
}
- mParent.addView(mGentleHeader, firstGentleNotifIndex);
- } else if (currentHeaderIndex != firstGentleNotifIndex - 1) {
- // Relocate the header to be immediately before the first child in the section
- int targetIndex = firstGentleNotifIndex;
- if (currentHeaderIndex < firstGentleNotifIndex) {
- // Adjust the target index to account for the header itself being temporarily
- // removed during the position change.
- targetIndex--;
- }
-
- mParent.changeViewPosition(mGentleHeader, targetIndex);
+ header.setContentVisible(true);
+ mParent.addView(header, targetIndex);
+ } else if (mParent.indexOfChild(header) != targetIndex) {
+ mParent.changeViewPosition(header, targetIndex);
}
+ return true;
}
}
- private int adjustPeopleHubVisibilityAndPosition(
- int targetIndex, boolean peopleNotificationsPresent) {
- final boolean showPeopleHeader = mNumberOfSections > 2
- && mStatusBarStateController.getState() != StatusBarState.KEYGUARD
- && (peopleNotificationsPresent || mPeopleHubVisible);
- final int currentHubIndex = mParent.indexOfChild(mPeopleHubView);
- final boolean currentlyVisible = currentHubIndex >= 0;
-
- mPeopleHubView.setCanSwipe(showPeopleHeader && !peopleNotificationsPresent);
-
- if (!showPeopleHeader) {
- if (currentlyVisible) {
- mParent.removeView(mPeopleHubView);
- return -1;
- }
- } else {
- mPeopleHubView.unDismiss();
- mPeopleHubView.resetTranslation();
- if (!currentlyVisible) {
- if (mPeopleHubView.getTransientContainer() != null) {
- mPeopleHubView.getTransientContainer().removeTransientView(mPeopleHubView);
- mPeopleHubView.setTransientContainer(null);
- }
- mParent.addView(mPeopleHubView, targetIndex);
- return 1;
- } else if (currentHubIndex != targetIndex) {
- if (currentHubIndex < targetIndex) {
- targetIndex--;
- }
- mParent.changeViewPosition(mPeopleHubView, targetIndex);
- }
- }
- return 0;
- }
-
/**
* Updates the boundaries (as tracked by their first and last views) of the priority sections.
*
@@ -388,7 +354,12 @@
//TODO: do this in a single pass, and more better
for (ActivatableNotificationView v : children) {
- if (getBucket(v) == filter) {
+ Integer bucket = getBucket(v);
+ if (bucket == null) {
+ throw new IllegalArgumentException("Cannot find section bucket for view");
+ }
+
+ if (bucket == filter) {
viewsInBucket.add(v);
}
@@ -463,16 +434,17 @@
/**
* For now, declare the available notification buckets (sections) here so that other
* presentation code can decide what to do based on an entry's buckets
- *
*/
@Retention(SOURCE)
@IntDef(prefix = { "BUCKET_" }, value = {
+ BUCKET_HEADS_UP,
BUCKET_PEOPLE,
BUCKET_ALERTING,
BUCKET_SILENT
})
public @interface PriorityBucket {}
- public static final int BUCKET_PEOPLE = 0;
- public static final int BUCKET_ALERTING = 1;
- public static final int BUCKET_SILENT = 2;
+ public static final int BUCKET_HEADS_UP = 0;
+ public static final int BUCKET_PEOPLE = 1;
+ public static final int BUCKET_ALERTING = 2;
+ public static final int BUCKET_SILENT = 3;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 2eeda1f..1bd9bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -815,7 +815,6 @@
mBgColor = mContext.getColor(R.color.notification_shade_background_color);
updateBackgroundDimming();
mShelf.onUiModeChanged();
- mSectionsManager.onUiModeChanged();
}
@ShadeViewRefactor(RefactorComponent.DECORATOR)
@@ -1632,8 +1631,8 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ExpandableView getChildAtPosition(float touchX, float touchY) {
- return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
-
+ return getChildAtPosition(
+ touchX, touchY, true /* requireMinHeight */, true /* ignoreDecors */);
}
/**
@@ -1642,17 +1641,18 @@
* @param touchX the x coordinate
* @param touchY the y coordinate
* @param requireMinHeight Whether a minimum height is required for a child to be returned.
+ * @param ignoreDecors Whether decors can be returned
* @return the child at the given location.
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ExpandableView getChildAtPosition(float touchX, float touchY,
- boolean requireMinHeight) {
+ boolean requireMinHeight, boolean ignoreDecors) {
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
for (int childIdx = 0; childIdx < count; childIdx++) {
ExpandableView slidingChild = (ExpandableView) getChildAt(childIdx);
if (slidingChild.getVisibility() != VISIBLE
- || slidingChild instanceof StackScrollerDecorView) {
+ || (ignoreDecors && slidingChild instanceof StackScrollerDecorView)) {
continue;
}
float childTop = slidingChild.getTranslationY();
@@ -4166,7 +4166,9 @@
case MotionEvent.ACTION_DOWN: {
final int y = (int) ev.getY();
mScrolledToTopOnFirstDown = isScrolledToTop();
- if (getChildAtPosition(ev.getX(), y, false /* requireMinHeight */) == null) {
+ final ExpandableView childAtTouchPos = getChildAtPosition(
+ ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */);
+ if (childAtTouchPos == null) {
setIsBeingDragged(false);
recycleVelocityTracker();
break;
@@ -6299,8 +6301,6 @@
}
if (view instanceof PeopleHubView) {
- PeopleHubView row = (PeopleHubView) view;
- row.dismiss(false);
mSectionsManager.hidePeopleRow();
}
@@ -6325,8 +6325,11 @@
@Override
public View getChildAtPosition(MotionEvent ev) {
- View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(),
- ev.getY());
+ View child = NotificationStackScrollLayout.this.getChildAtPosition(
+ ev.getX(),
+ ev.getY(),
+ true /* requireMinHeight */,
+ false /* ignoreDecors */);
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
ExpandableNotificationRow parent = row.getNotificationParent();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index e5717ae..151c6b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -25,30 +25,32 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
import com.android.systemui.statusbar.notification.people.DataListener
import com.android.systemui.statusbar.notification.people.PersonViewModel
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView
class PeopleHubView(context: Context, attrs: AttributeSet) :
- ActivatableNotificationView(context, attrs), SwipeableView {
+ StackScrollerDecorView(context, attrs), SwipeableView {
private lateinit var contents: ViewGroup
- private lateinit var personControllers: List<PersonDataListenerImpl>
- val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
- get() = personControllers.asSequence()
+ lateinit var personViewAdapters: Sequence<DataListener<PersonViewModel?>>
+ private set
override fun onFinishInflate() {
- super.onFinishInflate()
contents = requireViewById(R.id.people_list)
- personControllers = (0 until contents.childCount)
+ personViewAdapters = (0 until contents.childCount)
.reversed()
.asSequence()
.mapNotNull { idx ->
(contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl)
}
.toList()
+ .asSequence()
+ super.onFinishInflate()
+ setVisible(true /* nowVisible */, false /* animate */)
}
- override fun getContentView(): View = contents
+ override fun findContentView(): View = contents
+ override fun findSecondaryView(): View? = null
override fun hasFinishedInitialization(): Boolean = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index add982d..ad3ff69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.notification.stack;
import android.annotation.Nullable;
+import android.annotation.StringRes;
import android.content.Context;
-import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -28,7 +28,7 @@
import android.widget.TextView;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import java.util.Objects;
@@ -36,23 +36,22 @@
* Similar in size and appearance to the NotificationShelf, appears at the beginning of some
* notification sections. Currently only used for gentle notifications.
*/
-public class SectionHeaderView extends ActivatableNotificationView {
+public class SectionHeaderView extends StackScrollerDecorView {
private ViewGroup mContents;
private TextView mLabelView;
private ImageView mClearAllButton;
@Nullable private View.OnClickListener mOnClearClickListener = null;
- private final RectF mTmpRect = new RectF();
-
public SectionHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
- super.onFinishInflate();
mContents = Objects.requireNonNull(findViewById(R.id.content));
bindContents();
+ super.onFinishInflate();
+ setVisible(true /* nowVisible */, false /* animate */);
}
private void bindContents() {
@@ -64,15 +63,20 @@
}
@Override
- protected View getContentView() {
+ protected View findContentView() {
return mContents;
}
+ @Override
+ protected View findSecondaryView() {
+ return null;
+ }
+
/**
* Destroys and reinflates the visible contents of the section header. For use on configuration
* changes or any other time that layout values might need to be re-evaluated.
*
- * Does not reinflate the base content view itself ({@link #getContentView()} or any of the
+ * Does not reinflate the base content view itself ({@link #findContentView()} or any of the
* decorator views, such as the background view or shadow view.
*/
void reinflateContents() {
@@ -88,35 +92,20 @@
return true;
}
- /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */
- void onUiModeChanged() {
- updateBackgroundColors();
- mLabelView.setTextColor(
- getContext().getColor(R.color.notification_section_header_label_color));
- mClearAllButton.setImageResource(
- R.drawable.status_bar_notification_section_header_clear_btn);
- }
-
void setAreThereDismissableGentleNotifs(boolean areThereDismissableGentleNotifs) {
mClearAllButton.setVisibility(areThereDismissableGentleNotifs ? View.VISIBLE : View.GONE);
}
@Override
- protected boolean disallowSingleClick(MotionEvent event) {
- // Disallow single click on lockscreen if user is tapping on clear all button
- mTmpRect.set(
- mClearAllButton.getLeft(),
- mClearAllButton.getTop(),
- mClearAllButton.getLeft() + mClearAllButton.getWidth(),
- mClearAllButton.getTop() + mClearAllButton.getHeight());
- return mTmpRect.contains(event.getX(), event.getY());
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return super.onInterceptTouchEvent(ev);
}
/**
* Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything).
*/
void setOnHeaderClickListener(View.OnClickListener listener) {
- mContents.setOnClickListener(listener);
+ mLabelView.setOnClickListener(listener);
}
/** Fired when the user clicks on the "X" button on the far right of the header. */
@@ -124,4 +113,8 @@
mOnClearClickListener = listener;
mClearAllButton.setOnClickListener(listener);
}
+
+ void setHeaderText(@StringRes int resId) {
+ mLabelView.setText(resId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 691e1c4..1dde5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -150,14 +150,26 @@
private KeyguardViewMediator mKeyguardViewMediator;
private ScrimController mScrimController;
private StatusBar mStatusBar;
- private int mPendingAuthenticatedUserId = -1;
- private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
+ private PendingAuthenticated mPendingAuthenticated = null;
private boolean mPendingShowBouncer;
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
private final MetricsLogger mMetricsLogger;
+ private static final class PendingAuthenticated {
+ public final int userId;
+ public final BiometricSourceType biometricSourceType;
+ public final boolean isStrongBiometric;
+
+ PendingAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ this.userId = userId;
+ this.biometricSourceType = biometricSourceType;
+ this.isStrongBiometric = isStrongBiometric;
+ }
+ }
+
@Inject
public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
@@ -251,28 +263,30 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
- mPendingAuthenticatedUserId = userId;
- mPendingAuthenticatedBioSourceType = biometricSourceType;
+ mPendingAuthenticated = new PendingAuthenticated(userId, biometricSourceType,
+ isStrongBiometric);
Trace.endSection();
return;
}
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
boolean unlockAllowed = mKeyguardBypassController.onBiometricAuthenticated(
- biometricSourceType);
+ biometricSourceType, isStrongBiometric);
if (unlockAllowed) {
mKeyguardViewMediator.userActivity();
- startWakeAndUnlock(biometricSourceType);
+ startWakeAndUnlock(biometricSourceType, isStrongBiometric);
} else {
Log.d(TAG, "onBiometricAuthenticated aborted by bypass controller");
}
}
- public void startWakeAndUnlock(BiometricSourceType biometricSourceType) {
- startWakeAndUnlock(calculateMode(biometricSourceType));
+ public void startWakeAndUnlock(BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ startWakeAndUnlock(calculateMode(biometricSourceType, isStrongBiometric));
}
public void startWakeAndUnlock(@WakeAndUnlockMode int mode) {
@@ -373,45 +387,46 @@
public void onStartedGoingToSleep(int why) {
resetMode();
mFadedAwayAfterWakeAndUnlock = false;
- mPendingAuthenticatedUserId = -1;
- mPendingAuthenticatedBioSourceType = null;
+ mPendingAuthenticated = null;
}
@Override
public void onFinishedGoingToSleep(int why) {
Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
- BiometricSourceType pendingType = mPendingAuthenticatedBioSourceType;
- int pendingUserId = mPendingAuthenticatedUserId;
- if (pendingUserId != -1 && pendingType != null) {
+ if (mPendingAuthenticated != null) {
// Post this to make sure it's executed after the device is fully locked.
- mHandler.post(() -> onBiometricAuthenticated(pendingUserId, pendingType));
+ mHandler.post(() -> onBiometricAuthenticated(mPendingAuthenticated.userId,
+ mPendingAuthenticated.biometricSourceType,
+ mPendingAuthenticated.isStrongBiometric));
+ mPendingAuthenticated = null;
}
- mPendingAuthenticatedUserId = -1;
- mPendingAuthenticatedBioSourceType = null;
Trace.endSection();
}
public boolean hasPendingAuthentication() {
- return mPendingAuthenticatedUserId != -1
- && mUpdateMonitor.isUnlockingWithBiometricAllowed()
- && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
+ return mPendingAuthenticated != null
+ && mUpdateMonitor
+ .isUnlockingWithBiometricAllowed(mPendingAuthenticated.isStrongBiometric)
+ && mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
}
public int getMode() {
return mMode;
}
- private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType) {
+ private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (biometricSourceType == BiometricSourceType.FACE
|| biometricSourceType == BiometricSourceType.IRIS) {
- return calculateModeForPassiveAuth();
+ return calculateModeForPassiveAuth(isStrongBiometric);
} else {
- return calculateModeForFingerprint();
+ return calculateModeForFingerprint(isStrongBiometric);
}
}
- private @WakeAndUnlockMode int calculateModeForFingerprint() {
- boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
+ private @WakeAndUnlockMode int calculateModeForFingerprint(boolean isStrongBiometric) {
+ boolean unlockingAllowed =
+ mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric);
boolean deviceDreaming = mUpdateMonitor.isDreaming();
if (!mUpdateMonitor.isDeviceInteractive()) {
@@ -440,8 +455,9 @@
return MODE_NONE;
}
- private @WakeAndUnlockMode int calculateModeForPassiveAuth() {
- boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
+ private @WakeAndUnlockMode int calculateModeForPassiveAuth(boolean isStrongBiometric) {
+ boolean unlockingAllowed =
+ mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric);
boolean deviceDreaming = mUpdateMonitor.isDreaming();
boolean bypass = mKeyguardBypassController.getBypassEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index b4d0d47..03918e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -38,11 +38,20 @@
private val mKeyguardStateController: KeyguardStateController
private val statusBarStateController: StatusBarStateController
private var hasFaceFeature: Boolean
+ private var pendingUnlock: PendingUnlock? = null
/**
+ * Pending unlock info:
+ *
* The pending unlock type which is set if the bypass was blocked when it happened.
+ *
+ * Whether the pending unlock type is strong biometric or non-strong biometric
+ * (i.e. weak or convenience).
*/
- private var pendingUnlockType: BiometricSourceType? = null
+ private data class PendingUnlock(
+ val pendingUnlockType: BiometricSourceType,
+ val isStrongBiometric: Boolean
+ )
lateinit var unlockController: BiometricUnlockController
var isPulseExpanding = false
@@ -86,7 +95,7 @@
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStateChanged(newState: Int) {
if (newState != StatusBarState.KEYGUARD) {
- pendingUnlockType = null
+ pendingUnlock = null
}
}
})
@@ -101,7 +110,7 @@
lockscreenUserManager.addUserChangedListener(
object : NotificationLockscreenUserManager.UserChangedListener {
override fun onUserChanged(userId: Int) {
- pendingUnlockType = null
+ pendingUnlock = null
}
})
}
@@ -111,11 +120,14 @@
*
* @return false if we can not wake and unlock right now
*/
- fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean {
+ fun onBiometricAuthenticated(
+ biometricSourceType: BiometricSourceType,
+ isStrongBiometric: Boolean
+ ): Boolean {
if (bypassEnabled) {
val can = canBypass()
if (!can && (isPulseExpanding || qSExpanded)) {
- pendingUnlockType = biometricSourceType
+ pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric)
}
return can
}
@@ -123,10 +135,12 @@
}
fun maybePerformPendingUnlock() {
- if (pendingUnlockType != null) {
- if (onBiometricAuthenticated(pendingUnlockType!!)) {
- unlockController.startWakeAndUnlock(pendingUnlockType)
- pendingUnlockType = null
+ if (pendingUnlock != null) {
+ if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType,
+ pendingUnlock!!.isStrongBiometric)) {
+ unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType,
+ pendingUnlock!!.isStrongBiometric)
+ pendingUnlock = null
}
}
}
@@ -162,12 +176,17 @@
}
fun onStartedGoingToSleep() {
- pendingUnlockType = null
+ pendingUnlock = null
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("KeyguardBypassController:")
- pw.println(" pendingUnlockType: $pendingUnlockType")
+ if (pendingUnlock != null) {
+ pw.println(" mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}")
+ pw.println(" mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}")
+ } else {
+ pw.println(" mPendingUnlock: $pendingUnlock")
+ }
pw.println(" bypassEnabled: $bypassEnabled")
pw.println(" canBypass: ${canBypass()}")
pw.println(" bouncerShowing: $bouncerShowing")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 60589843..61cef68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -387,7 +387,12 @@
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- boolean unlockingAllowed = mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed();
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed
+ boolean unlockingAllowed = mKeyguardUpdateMonitor
+ .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
if (fingerprintRunning && unlockingAllowed) {
AccessibilityNodeInfo.AccessibilityAction unlock
= new AccessibilityNodeInfo.AccessibilityAction(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index fb7976f..c61d7bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -202,8 +202,10 @@
@Override
public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
- if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ if (mFirstBypassAttempt
+ && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
mDelayShowingKeyguardStatusBar = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 260f94c..1ab36c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -678,6 +678,12 @@
}
@Override
+ public void onCountdownEnd() {
+ if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown");
+ mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
+ }
+
+ @Override
public void onRecordingStart() {
if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
mIconController.setIcon(mSlotScreenRecord,
@@ -687,7 +693,7 @@
@Override
public void onRecordingEnd() {
- // Ensure this is on the main thread, since it could be called during countdown
+ // Ensure this is on the main thread
if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 10821d6..945a9db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -21,7 +21,6 @@
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.app.AlarmManager;
-import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -45,7 +44,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -116,7 +114,7 @@
* A scrim varies its opacity based on a busyness factor, for example
* how many notifications are currently visible.
*/
- public static final float BUSY_SCRIM_ALPHA = 0.54f;
+ public static final float BUSY_SCRIM_ALPHA = 0.75f;
/**
* The most common scrim, the one under the keyguard.
@@ -146,8 +144,6 @@
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
- private float mScrimBehindAlpha;
- private float mScrimBehindAlphaResValue;
private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
// Assuming the shade is expanded during initialization
@@ -192,7 +188,6 @@
@Inject
public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters,
AlarmManager alarmManager, KeyguardStateController keyguardStateController,
- @Main Resources resources,
DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor,
DockManager dockManager) {
@@ -203,14 +198,12 @@
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
- mScrimBehindAlphaResValue = resources.getFloat(R.dimen.scrim_behind_alpha);
mHandler = handler;
mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
"hide_aod_wallpaper", mHandler);
mWakeLock = delayedWakeLockBuilder.setHandler(mHandler).setTag("Scrims").build();
// Scrim alpha is initially set to the value on the resource but might be changed
// to make sure that text on top of it is legible.
- mScrimBehindAlpha = mScrimBehindAlphaResValue;
mDozeParameters = dozeParameters;
mDockManager = dockManager;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -587,7 +580,6 @@
int mainColor = mColors.getMainColor();
float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor,
4.5f /* minimumContrast */) / 255f;
- mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity);
dispatchScrimState(mScrimBehind.getViewAlpha());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 0ab08a8..a7f60d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -293,13 +293,12 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
- if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
- Trace.endSection();
- return;
+ if (mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
+ update(false /* updateAlways */);
}
- update(false /* updateAlways */);
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index f4157f2..8625d63 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -126,6 +126,13 @@
internal var startAction: () -> Unit = ::startInternal
/**
+ * Action to run when [cancel] is called. This can be changed by
+ * [PhysicsAnimatorTestUtils.prepareForTest] to cancel animations from the main thread, which
+ * is required.
+ */
+ internal var cancelAction: (Set<FloatPropertyCompat<in T>>) -> Unit = ::cancelInternal
+
+ /**
* Springs a property to the given value, using the provided configuration settings.
*
* Springs are used when you know the exact value to which you want to animate. They can be
@@ -429,10 +436,13 @@
max = max(currentValue, this.max)
}
- // Apply the configuration and start the animation. Since flings can't be
- // redirected while in motion, cancel it first.
+ // Flings can't be updated to a new position while maintaining velocity, because
+ // we're using the explicitly provided start velocity. Cancel any flings (or
+ // springs) on this property before flinging.
+ cancel(animatedProperty)
+
+ // Apply the configuration and start the animation.
getFlingAnimation(animatedProperty)
- .also { it.cancel() }
.also { flingConfig.applyToAnimation(it) }
.start()
}
@@ -707,11 +717,26 @@
return springConfigs.keys.union(flingConfigs.keys)
}
+ /**
+ * Cancels the given properties. This is typically called immediately by [cancel], unless this
+ * animator is under test.
+ */
+ internal fun cancelInternal(properties: Set<FloatPropertyCompat<in T>>) {
+ for (property in properties) {
+ flingAnimations[property]?.cancel()
+ springAnimations[property]?.cancel()
+ }
+ }
+
/** Cancels all in progress animations on all properties. */
fun cancel() {
- for (dynamicAnim in flingAnimations.values.union(springAnimations.values)) {
- dynamicAnim.cancel()
- }
+ cancelAction(flingAnimations.keys)
+ cancelAction(springAnimations.keys)
+ }
+
+ /** Cancels in progress animations on the provided properties only. */
+ fun cancel(vararg properties: FloatPropertyCompat<in T>) {
+ cancelAction(properties.toSet())
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
index 965decd..c50eeac 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimatorTestUtils.kt
@@ -363,8 +363,12 @@
private val testEndListeners = ArrayList<PhysicsAnimator.EndListener<T>>()
private val testUpdateListeners = ArrayList<PhysicsAnimator.UpdateListener<T>>()
+ /** Whether we're currently in the middle of executing startInternal(). */
+ private var currentlyRunningStartInternal = false
+
init {
animator.startAction = ::startForTest
+ animator.cancelAction = ::cancelForTest
}
internal fun addTestEndListener(listener: PhysicsAnimator.EndListener<T>) {
@@ -437,7 +441,29 @@
}
})
+ currentlyRunningStartInternal = true
animator.startInternal()
+ currentlyRunningStartInternal = false
+ unblockLatch.countDown()
+ }
+
+ unblockLatch.await(timeoutMs, TimeUnit.MILLISECONDS)
+ }
+
+ private fun cancelForTest(properties: Set<FloatPropertyCompat<in T>>) {
+ // If this was called from startInternal, we are already on the animation thread, and
+ // should just call cancelInternal rather than posting it. If we post it, the
+ // cancellation will occur after the rest of startInternal() and we'll immediately
+ // cancel the animation we worked so hard to start!
+ if (currentlyRunningStartInternal) {
+ animator.cancelInternal(properties)
+ return
+ }
+
+ val unblockLatch = CountDownLatch(1)
+
+ animationThreadHandler.post {
+ animator.cancelInternal(properties)
unblockLatch.countDown()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
new file mode 100644
index 0000000..2276ba1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemui.util.magnetictarget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.database.ContentObserver
+import android.graphics.PointF
+import android.os.Handler
+import android.os.UserHandle
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.provider.Settings
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.systemui.util.animation.PhysicsAnimator
+import kotlin.math.hypot
+
+/**
+ * Utility class for creating 'magnetized' objects that are attracted to one or more magnetic
+ * targets. Magnetic targets attract objects that are dragged near them, and hold them there unless
+ * they're moved away or released. Releasing objects inside a magnetic target typically performs an
+ * action on the object.
+ *
+ * MagnetizedObject also supports flinging to targets, which will result in the object being pulled
+ * into the target and released as if it was dragged into it.
+ *
+ * To use this class, either construct an instance with an object of arbitrary type, or use the
+ * [MagnetizedObject.magnetizeView] shortcut method if you're magnetizing a view. Then, set
+ * [magnetListener] to receive event callbacks. In your touch handler, pass all MotionEvents
+ * that move this object to [maybeConsumeMotionEvent]. If that method returns true, consider the
+ * event consumed by the MagnetizedObject and don't move the object unless it begins returning false
+ * again.
+ *
+ * @param context Context, used to retrieve a Vibrator instance for vibration effects.
+ * @param underlyingObject The actual object that we're magnetizing.
+ * @param xProperty Property that sets the x value of the object's position.
+ * @param yProperty Property that sets the y value of the object's position.
+ */
+abstract class MagnetizedObject<T : Any>(
+ val context: Context,
+
+ /** The actual object that is animated. */
+ val underlyingObject: T,
+
+ /** Property that gets/sets the object's X value. */
+ val xProperty: FloatPropertyCompat<in T>,
+
+ /** Property that gets/sets the object's Y value. */
+ val yProperty: FloatPropertyCompat<in T>
+) {
+
+ /** Return the width of the object. */
+ abstract fun getWidth(underlyingObject: T): Float
+
+ /** Return the height of the object. */
+ abstract fun getHeight(underlyingObject: T): Float
+
+ /**
+ * Fill the provided array with the location of the top-left of the object, relative to the
+ * entire screen. Compare to [View.getLocationOnScreen].
+ */
+ abstract fun getLocationOnScreen(underlyingObject: T, loc: IntArray)
+
+ /** Methods for listening to events involving a magnetized object. */
+ interface MagnetListener {
+
+ /**
+ * Called when touch events move within the magnetic field of a target, causing the
+ * object to animate to the target and become 'stuck' there. The animation happens
+ * automatically here - you should not move the object. You can, however, change its state
+ * to indicate to the user that it's inside the target and releasing it will have an effect.
+ *
+ * [maybeConsumeMotionEvent] is now returning true and will continue to do so until a call
+ * to [onUnstuckFromTarget] or [onReleasedInTarget].
+ *
+ * @param target The target that the object is now stuck to.
+ */
+ fun onStuckToTarget(target: MagneticTarget)
+
+ /**
+ * Called when the object is no longer stuck to a target. This means that either touch
+ * events moved outside of the magnetic field radius, or that a forceful fling out of the
+ * target was detected.
+ *
+ * The object won't be automatically animated out of the target, since you're responsible
+ * for moving the object again. You should move it (or animate it) using your own
+ * movement/animation logic.
+ *
+ * Reverse any effects applied in [onStuckToTarget] here.
+ *
+ * If [wasFlungOut] is true, [maybeConsumeMotionEvent] returned true for the ACTION_UP event
+ * that concluded the fling. If [wasFlungOut] is false, that means a drag gesture is ongoing
+ * and [maybeConsumeMotionEvent] is now returning false.
+ *
+ * @param target The target that this object was just unstuck from.
+ * @param velX The X velocity of the touch gesture when it exited the magnetic field.
+ * @param velY The Y velocity of the touch gesture when it exited the magnetic field.
+ * @param wasFlungOut Whether the object was unstuck via a fling gesture. This means that
+ * an ACTION_UP event was received, and that the gesture velocity was sufficient to conclude
+ * that the user wants to un-stick the object despite no touch events occurring outside of
+ * the magnetic field radius.
+ */
+ fun onUnstuckFromTarget(
+ target: MagneticTarget,
+ velX: Float,
+ velY: Float,
+ wasFlungOut: Boolean
+ )
+
+ /**
+ * Called when the object is released inside a target, or flung towards it with enough
+ * velocity to reach it.
+ *
+ * @param target The target that the object was released in.
+ */
+ fun onReleasedInTarget(target: MagneticTarget)
+ }
+
+ private val animator: PhysicsAnimator<T> = PhysicsAnimator.getInstance(underlyingObject)
+ private val objectLocationOnScreen = IntArray(2)
+
+ /**
+ * Targets that have been added to this object. These will all be considered when determining
+ * magnetic fields and fling trajectories.
+ */
+ private val associatedTargets = ArrayList<MagneticTarget>()
+
+ private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
+ private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+
+ /** Whether touch events are presently occurring within the magnetic field area of a target. */
+ val objectStuckToTarget: Boolean
+ get() = targetObjectIsStuckTo != null
+
+ /** The target the object is stuck to, or null if the object is not stuck to any target. */
+ private var targetObjectIsStuckTo: MagneticTarget? = null
+
+ /**
+ * Sets the listener to receive events. This must be set, or [maybeConsumeMotionEvent]
+ * will always return false and no magnetic effects will occur.
+ */
+ lateinit var magnetListener: MagnetizedObject.MagnetListener
+
+ /**
+ * Sets whether forcefully flinging the object vertically towards a target causes it to be
+ * attracted to the target and then released immediately, despite never being dragged within the
+ * magnetic field.
+ */
+ var flingToTargetEnabled = true
+
+ /**
+ * If fling to target is enabled, forcefully flinging the object towards a target will cause
+ * it to be attracted to the target and then released immediately, despite never being dragged
+ * within the magnetic field.
+ *
+ * This sets the width of the area considered 'near' enough a target to be considered a fling,
+ * in terms of percent of the target view's width. For example, setting this to 3f means that
+ * flings towards a 100px-wide target will be considered 'near' enough if they're towards the
+ * 300px-wide area around the target.
+ *
+ * Flings whose trajectory intersects the area will be attracted and released - even if the
+ * target view itself isn't intersected:
+ *
+ * | |
+ * | 0 |
+ * | / |
+ * | / |
+ * | X / |
+ * |.....###.....|
+ *
+ *
+ * Flings towards the target whose trajectories do not intersect the area will be treated as
+ * normal flings and the magnet will leave the object alone:
+ *
+ * | |
+ * | |
+ * | 0 |
+ * | / |
+ * | / X |
+ * |.....###.....|
+ *
+ */
+ var flingToTargetWidthPercent = 3f
+
+ /**
+ * Sets the minimum velocity (in pixels per second) required to fling an object to the target
+ * without dragging it into the magnetic field.
+ */
+ var flingToTargetMinVelocity = 4000f
+
+ /**
+ * Sets the minimum velocity (in pixels per second) required to fling un-stuck an object stuck
+ * to the target. If this velocity is reached, the object will be freed even if it wasn't moved
+ * outside the magnetic field radius.
+ */
+ var flingUnstuckFromTargetMinVelocity = 1000f
+
+ /**
+ * Sets the maximum velocity above which the object will not stick to the target. Even if the
+ * object is dragged through the magnetic field, it will not stick to the target until the
+ * velocity is below this value.
+ */
+ var stickToTargetMaxVelocity = 2000f
+
+ /**
+ * Enable or disable haptic vibration effects when the object interacts with the magnetic field.
+ *
+ * If you're experiencing crashes when the object enters targets, ensure that you have the
+ * android.permission.VIBRATE permission!
+ */
+ var hapticsEnabled = true
+
+ /** Whether the HAPTIC_FEEDBACK_ENABLED setting is true. */
+ private var systemHapticsEnabled = false
+
+ /** Default spring configuration to use for animating the object into a target. */
+ var springConfig = PhysicsAnimator.SpringConfig(
+ SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY)
+
+ /**
+ * Spring configuration to use to spring the object into a target specifically when it's flung
+ * towards (rather than dragged near) it.
+ */
+ var flungIntoTargetSpringConfig = springConfig
+
+ init {
+ val hapticSettingObserver =
+ object : ContentObserver(Handler.getMain()) {
+ override fun onChange(selfChange: Boolean) {
+ systemHapticsEnabled =
+ Settings.System.getIntForUser(
+ context.contentResolver,
+ Settings.System.HAPTIC_FEEDBACK_ENABLED,
+ 0,
+ UserHandle.USER_CURRENT) != 0
+ }
+ }
+
+ context.contentResolver.registerContentObserver(
+ Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED),
+ true /* notifyForDescendants */, hapticSettingObserver)
+
+ // Trigger the observer once to initialize systemHapticsEnabled.
+ hapticSettingObserver.onChange(false /* selfChange */)
+ }
+
+ /**
+ * Adds the provided MagneticTarget to this object. The object will now be attracted to the
+ * target if it strays within its magnetic field or is flung towards it.
+ *
+ * If this target (or its magnetic field) overlaps another target added to this object, the
+ * prior target will take priority.
+ */
+ fun addTarget(target: MagneticTarget) {
+ associatedTargets.add(target)
+ target.updateLocationOnScreen()
+ }
+
+ /**
+ * Shortcut that accepts a View and a magnetic field radius and adds it as a magnetic target.
+ *
+ * @return The MagneticTarget instance for the given View. This can be used to change the
+ * target's magnetic field radius after it's been added. It can also be added to other
+ * magnetized objects.
+ */
+ fun addTarget(target: View, magneticFieldRadiusPx: Int): MagneticTarget {
+ return MagneticTarget(target, magneticFieldRadiusPx).also { addTarget(it) }
+ }
+
+ /**
+ * Removes the given target from this object. The target will no longer attract the object.
+ */
+ fun removeTarget(target: MagneticTarget) {
+ associatedTargets.remove(target)
+ }
+
+ /**
+ * Provide this method with all motion events that move the magnetized object. If the
+ * location of the motion events moves within the magnetic field of a target, or indicate a
+ * fling-to-target gesture, this method will return true and you should not move the object
+ * yourself until it returns false again.
+ *
+ * Note that even when this method returns true, you should continue to pass along new motion
+ * events so that we know when the events move back outside the magnetic field area.
+ *
+ * This method will always return false if you haven't set a [magnetListener].
+ */
+ fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean {
+ // Short-circuit if we don't have a listener or any targets, since those are required.
+ if (associatedTargets.size == 0) {
+ return false
+ }
+
+ // When a gesture begins, recalculate target views' positions on the screen in case they
+ // have changed. Also, clear state.
+ if (ev.action == MotionEvent.ACTION_DOWN) {
+ updateTargetViewLocations()
+
+ // Clear the velocity tracker and assume we're not stuck to a target yet.
+ velocityTracker.clear()
+ targetObjectIsStuckTo = null
+ }
+
+ addMovement(ev)
+
+ val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
+ val distanceFromTargetCenter = hypot(
+ ev.rawX - target.centerOnScreen.x,
+ ev.rawY - target.centerOnScreen.y)
+ distanceFromTargetCenter < target.magneticFieldRadiusPx
+ }
+
+ // If we aren't currently stuck to a target, and we're in the magnetic field of a target,
+ // we're newly stuck.
+ val objectNewlyStuckToTarget =
+ !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null
+
+ // If we are currently stuck to a target, we're in the magnetic field of a target, and that
+ // target isn't the one we're currently stuck to, then touch events have moved into a
+ // adjacent target's magnetic field.
+ val objectMovedIntoDifferentTarget =
+ objectStuckToTarget &&
+ targetObjectIsInMagneticFieldOf != null &&
+ targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf
+
+ if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) {
+ velocityTracker.computeCurrentVelocity(1000)
+ val velX = velocityTracker.xVelocity
+ val velY = velocityTracker.yVelocity
+
+ // If the object is moving too quickly within the magnetic field, do not stick it. This
+ // only applies to objects newly stuck to a target. If the object is moved into a new
+ // target, it wasn't moving at all (since it was stuck to the previous one).
+ if (objectNewlyStuckToTarget && hypot(velX, velY) > stickToTargetMaxVelocity) {
+ return false
+ }
+
+ // This touch event is newly within the magnetic field - let the listener know, and
+ // animate sticking to the magnet.
+ targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
+ cancelAnimations()
+ magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
+ animateStuckToTarget(targetObjectIsInMagneticFieldOf!!, velX, velY, false)
+
+ vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+ } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
+ velocityTracker.computeCurrentVelocity(1000)
+
+ // This touch event is newly outside the magnetic field - let the listener know. It will
+ // move the object out of the target using its own movement logic.
+ cancelAnimations()
+ magnetListener.onUnstuckFromTarget(
+ targetObjectIsStuckTo!!, velocityTracker.xVelocity, velocityTracker.yVelocity,
+ wasFlungOut = false)
+ targetObjectIsStuckTo = null
+
+ vibrateIfEnabled(VibrationEffect.EFFECT_TICK)
+ }
+
+ // First, check for relevant gestures concluding with an ACTION_UP.
+ if (ev.action == MotionEvent.ACTION_UP) {
+
+ velocityTracker.computeCurrentVelocity(1000 /* units */)
+ val velX = velocityTracker.xVelocity
+ val velY = velocityTracker.yVelocity
+
+ // Cancel the magnetic animation since we might still be springing into the magnetic
+ // target, but we're about to fling away or release.
+ cancelAnimations()
+
+ if (objectStuckToTarget) {
+ if (hypot(velX, velY) > flingUnstuckFromTargetMinVelocity) {
+ // If the object is stuck, but it was forcefully flung away from the target,
+ // tell the listener so the object can be animated out of the target.
+ magnetListener.onUnstuckFromTarget(
+ targetObjectIsStuckTo!!, velX, velY, wasFlungOut = true)
+ } else {
+ // If the object is stuck and not flung away, it was released inside the target.
+ magnetListener.onReleasedInTarget(targetObjectIsStuckTo!!)
+ vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+ }
+
+ // Either way, we're no longer stuck.
+ targetObjectIsStuckTo = null
+ return true
+ }
+
+ // The target we're flinging towards, or null if we're not flinging towards any target.
+ val flungToTarget = associatedTargets.firstOrNull { target ->
+ isForcefulFlingTowardsTarget(target, ev.rawX, ev.rawY, velX, velY)
+ }
+
+ if (flungToTarget != null) {
+ // If this is a fling-to-target, animate the object to the magnet and then release
+ // it.
+ magnetListener.onStuckToTarget(flungToTarget)
+ targetObjectIsStuckTo = flungToTarget
+
+ animateStuckToTarget(flungToTarget, velX, velY, true) {
+ targetObjectIsStuckTo = null
+ magnetListener.onReleasedInTarget(flungToTarget)
+ vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+ }
+
+ return true
+ }
+
+ // If it's not either of those things, we are not interested.
+ return false
+ }
+
+ return objectStuckToTarget // Always consume touch events if the object is stuck.
+ }
+
+ /** Plays the given vibration effect if haptics are enabled. */
+ @SuppressLint("MissingPermission")
+ private fun vibrateIfEnabled(effect: Int) {
+ if (hapticsEnabled && systemHapticsEnabled) {
+ vibrator.vibrate(effect.toLong())
+ }
+ }
+
+ /** Adds the movement to the velocity tracker using raw coordinates. */
+ private fun addMovement(event: MotionEvent) {
+ // Add movement to velocity tracker using raw screen X and Y coordinates instead
+ // of window coordinates because the window frame may be moving at the same time.
+ val deltaX = event.rawX - event.x
+ val deltaY = event.rawY - event.y
+ event.offsetLocation(deltaX, deltaY)
+ velocityTracker.addMovement(event)
+ event.offsetLocation(-deltaX, -deltaY)
+ }
+
+ /** Animates sticking the object to the provided target with the given start velocities. */
+ private fun animateStuckToTarget(
+ target: MagneticTarget,
+ velX: Float,
+ velY: Float,
+ flung: Boolean,
+ after: (() -> Unit)? = null
+ ) {
+ target.updateLocationOnScreen()
+ getLocationOnScreen(underlyingObject, objectLocationOnScreen)
+
+ // Calculate the difference between the target's center coordinates and the object's.
+ // Animating the object's x/y properties by these values will center the object on top
+ // of the magnetic target.
+ val xDiff = target.centerOnScreen.x -
+ getWidth(underlyingObject) / 2f - objectLocationOnScreen[0]
+ val yDiff = target.centerOnScreen.y -
+ getHeight(underlyingObject) / 2f - objectLocationOnScreen[1]
+
+ val springConfig = if (flung) flungIntoTargetSpringConfig else springConfig
+
+ cancelAnimations()
+
+ // Animate to the center of the target.
+ animator
+ .spring(xProperty, xProperty.getValue(underlyingObject) + xDiff, velX,
+ springConfig)
+ .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY,
+ springConfig)
+
+ if (after != null) {
+ animator.withEndActions(after)
+ }
+
+ animator.start()
+ }
+
+ /**
+ * Whether or not the provided values match a 'fast fling' towards the provided target. If it
+ * does, we consider it a fling-to-target gesture.
+ */
+ private fun isForcefulFlingTowardsTarget(
+ target: MagneticTarget,
+ rawX: Float,
+ rawY: Float,
+ velX: Float,
+ velY: Float
+ ): Boolean {
+ if (!flingToTargetEnabled) {
+ return false
+ }
+
+ // Whether velocity is sufficient, depending on whether we're flinging into a target at the
+ // top or the bottom of the screen.
+ val velocitySufficient =
+ if (rawY < target.centerOnScreen.y) velY > flingToTargetMinVelocity
+ else velY < flingToTargetMinVelocity
+
+ if (!velocitySufficient) {
+ return false
+ }
+
+ // Whether the trajectory of the fling intersects the target area.
+ var targetCenterXIntercept = rawX
+
+ // Only do math if the X velocity is non-zero, otherwise X won't change.
+ if (velX != 0f) {
+ // Rise over run...
+ val slope = velY / velX
+ // ...y = mx + b, b = y / mx...
+ val yIntercept = rawY - slope * rawX
+
+ // ...calculate the x value when y = the target's y-coordinate.
+ targetCenterXIntercept = (target.centerOnScreen.y - yIntercept) / slope
+ }
+
+ // The width of the area we're looking for a fling towards.
+ val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent
+
+ // Velocity was sufficient, so return true if the intercept is within the target area.
+ return targetCenterXIntercept > target.centerOnScreen.x - targetAreaWidth / 2 &&
+ targetCenterXIntercept < target.centerOnScreen.x + targetAreaWidth / 2
+ }
+
+ /** Cancel animations on this object's x/y properties. */
+ internal fun cancelAnimations() {
+ animator.cancel(xProperty, yProperty)
+ }
+
+ /** Updates the locations on screen of all of the [associatedTargets]. */
+ internal fun updateTargetViewLocations() {
+ associatedTargets.forEach { it.updateLocationOnScreen() }
+ }
+
+ /**
+ * Represents a target view with a magnetic field radius and cached center-on-screen
+ * coordinates.
+ *
+ * Instances of MagneticTarget are passed to a MagnetizedObject's [addTarget], and can then
+ * attract the object if it's dragged near or flung towards it. MagneticTargets can be added to
+ * multiple objects.
+ */
+ class MagneticTarget(
+ internal val targetView: View,
+ var magneticFieldRadiusPx: Int
+ ) {
+ internal val centerOnScreen = PointF()
+
+ private val tempLoc = IntArray(2)
+
+ fun updateLocationOnScreen() {
+ targetView.getLocationOnScreen(tempLoc)
+
+ // Add half of the target size to get the center, and subtract translation since the
+ // target could be animating in while we're doing this calculation.
+ centerOnScreen.set(
+ tempLoc[0] + targetView.width / 2f - targetView.translationX,
+ tempLoc[1] + targetView.height / 2f - targetView.translationY)
+ }
+ }
+
+ companion object {
+
+ /**
+ * Magnetizes the given view. Magnetized views are attracted to one or more magnetic
+ * targets. Magnetic targets attract objects that are dragged near them, and hold them there
+ * unless they're moved away or released. Releasing objects inside a magnetic target
+ * typically performs an action on the object.
+ *
+ * Magnetized views can also be flung to targets, which will result in the view being pulled
+ * into the target and released as if it was dragged into it.
+ *
+ * To use the returned MagnetizedObject<View> instance, first set [magnetListener] to
+ * receive event callbacks. In your touch handler, pass all MotionEvents that move this view
+ * to [maybeConsumeMotionEvent]. If that method returns true, consider the event consumed by
+ * MagnetizedObject and don't move the view unless it begins returning false again.
+ *
+ * The view will be moved via translationX/Y properties, and its
+ * width/height will be determined via getWidth()/getHeight(). If you are animating
+ * something other than a view, or want to position your view using properties other than
+ * translationX/Y, implement an instance of [MagnetizedObject].
+ *
+ * Note that the magnetic library can't re-order your view automatically. If the view
+ * renders on top of the target views, it will obscure the target when it sticks to it.
+ * You'll want to bring the view to the front in [MagnetListener.onStuckToTarget].
+ */
+ @JvmStatic
+ fun <T : View> magnetizeView(view: T): MagnetizedObject<T> {
+ return object : MagnetizedObject<T>(
+ view.context,
+ view,
+ DynamicAnimation.TRANSLATION_X,
+ DynamicAnimation.TRANSLATION_Y) {
+ override fun getWidth(underlyingObject: T): Float {
+ return underlyingObject.width.toFloat()
+ }
+
+ override fun getHeight(underlyingObject: T): Float {
+ return underlyingObject.height.toFloat() }
+
+ override fun getLocationOnScreen(underlyingObject: T, loc: IntArray) {
+ underlyingObject.getLocationOnScreen(loc)
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
new file mode 100644
index 0000000..a94af24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 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 com.android.systemui.wifi;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.IAdbManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Alerts the user of an untrusted network when enabling wireless debugging.
+ * The user can either deny, allow, or allow with the "always allow on this
+ * network" checked.
+ */
+public class WifiDebuggingActivity extends AlertActivity
+ implements DialogInterface.OnClickListener {
+ private static final String TAG = "WifiDebuggingActivity";
+
+ private CheckBox mAlwaysAllow;
+ // Notifies when wifi is disabled, or the network changed
+ private WifiChangeReceiver mWifiChangeReceiver;
+ private WifiManager mWifiManager;
+ private String mBssid;
+ private boolean mClicked = false;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ Window window = getWindow();
+ window.addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+
+ super.onCreate(icicle);
+
+
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ mWifiChangeReceiver = new WifiChangeReceiver(this);
+
+ Intent intent = getIntent();
+ String ssid = intent.getStringExtra("ssid");
+ mBssid = intent.getStringExtra("bssid");
+
+ if (ssid == null || mBssid == null) {
+ finish();
+ return;
+ }
+
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.wifi_debugging_title);
+ ap.mMessage = getString(R.string.wifi_debugging_message, ssid, mBssid);
+ ap.mPositiveButtonText = getString(R.string.wifi_debugging_allow);
+ ap.mNegativeButtonText = getString(android.R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+
+ // add "always allow" checkbox
+ LayoutInflater inflater = LayoutInflater.from(ap.mContext);
+ View checkbox = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysAllow = (CheckBox) checkbox.findViewById(com.android.internal.R.id.alwaysUse);
+ mAlwaysAllow.setText(getString(R.string.wifi_debugging_always));
+ ap.mView = checkbox;
+ window.setCloseOnTouchOutside(false);
+
+ setupAlert();
+
+ // adding touch listener on affirmative button - checks if window is obscured
+ // if obscured, do not let user give permissions (could be tapjacking involved)
+ final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> {
+ // Filter obscured touches by consuming them.
+ if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
+ || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ // TODO: need a different value for safety net?
+ EventLog.writeEvent(0x534e4554, "62187985"); // safety net logging
+ Toast.makeText(v.getContext(),
+ R.string.touch_filtered_warning,
+ Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ }
+ return false;
+ };
+ mAlert.getButton(BUTTON_POSITIVE).setOnTouchListener(filterTouchListener);
+
+ }
+
+ @Override
+ public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
+ super.onWindowAttributesChanged(params);
+ }
+
+ private class WifiChangeReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ WifiChangeReceiver(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void onReceive(Context content, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ mActivity.finish();
+ }
+ } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (!networkInfo.isConnected()) {
+ mActivity.finish();
+ return;
+ }
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+ mActivity.finish();
+ return;
+ }
+ String bssid = wifiInfo.getBSSID();
+ if (bssid == null || bssid.isEmpty()) {
+ mActivity.finish();
+ return;
+ }
+ if (!bssid.equals(mBssid)) {
+ mActivity.finish();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ registerReceiver(mWifiChangeReceiver, filter);
+ }
+
+ @Override
+ protected void onStop() {
+ if (mWifiChangeReceiver != null) {
+ unregisterReceiver(mWifiChangeReceiver);
+ }
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ // In the case where user dismissed the dialog, we don't get an onClick event.
+ // In that case, tell adb to deny the network connection.
+ if (!mClicked) {
+ try {
+ IBinder b = ServiceManager.getService(ADB_SERVICE);
+ IAdbManager service = IAdbManager.Stub.asInterface(b);
+ service.denyWirelessDebugging();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to notify Adb service", e);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mClicked = true;
+ boolean allow = (which == AlertDialog.BUTTON_POSITIVE);
+ boolean alwaysAllow = allow && mAlwaysAllow.isChecked();
+ try {
+ IBinder b = ServiceManager.getService(ADB_SERVICE);
+ IAdbManager service = IAdbManager.Stub.asInterface(b);
+ if (allow) {
+ service.allowWirelessDebugging(alwaysAllow, mBssid);
+ } else {
+ service.denyWirelessDebugging();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to notify Adb service", e);
+ }
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
new file mode 100644
index 0000000..0266a84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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 com.android.systemui.wifi;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Alerts the user that wireless debugging cannot be enabled by a secondary user.
+ */
+public class WifiDebuggingSecondaryUserActivity extends AlertActivity
+ implements DialogInterface.OnClickListener {
+ private WifiChangeReceiver mWifiChangeReceiver;
+ private WifiManager mWifiManager;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ mWifiChangeReceiver = new WifiChangeReceiver(this);
+
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.wifi_debugging_secondary_user_title);
+ ap.mMessage = getString(R.string.wifi_debugging_secondary_user_message);
+ ap.mPositiveButtonText = getString(android.R.string.ok);
+ ap.mPositiveButtonListener = this;
+
+ setupAlert();
+ }
+
+ private class WifiChangeReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ WifiChangeReceiver(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void onReceive(Context content, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ mActivity.finish();
+ }
+ } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (!networkInfo.isConnected()) {
+ mActivity.finish();
+ return;
+ }
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+ mActivity.finish();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ registerReceiver(mWifiChangeReceiver, filter);
+ }
+
+ @Override
+ protected void onStop() {
+ if (mWifiChangeReceiver != null) {
+ unregisterReceiver(mWifiChangeReceiver);
+ }
+ super.onStop();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index e5f56d4..38da21e 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -46,6 +46,9 @@
# UI it doesn't own. This is necessary to allow screenshots to be taken
LOCAL_CERTIFICATE := platform
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest.xml
+LOCAL_MANIFEST_FILE := AndroidManifest-base.xml
+
# Provide jack a list of classes to exclude from code coverage.
# This is needed because the SystemUITests compile SystemUI source directly, rather than using
# LOCAL_INSTRUMENTATION_FOR := SystemUI.
diff --git a/packages/SystemUI/tests/AndroidManifest-base.xml b/packages/SystemUI/tests/AndroidManifest-base.xml
new file mode 100644
index 0000000..f08d3d2
--- /dev/null
+++ b/packages/SystemUI/tests/AndroidManifest-base.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:sharedUserId="android.uid.system"
+ package="com.android.systemui.tests" />
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c51624b..12c7048 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:sharedUserId="android.uid.system"
- package="com.android.systemui.tests">
+ package="com.android.systemui" >
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
@@ -55,11 +55,6 @@
<application android:debuggable="true" android:largeHeap="true">
<uses-library android:name="android.test.runner" />
- <activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" />
-
- <service
- android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService"
- android:process=":killable" />
<receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver">
<intent-filter>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index b6ca8d8e..7231b8a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -41,8 +41,8 @@
InjectionInflationController inflationController = new InjectionInflationController(
SystemUIFactory.getInstance().getRootComponent());
Context context = getContext();
- KeyguardPresentation keyguardPresentation =
- new KeyguardPresentation(context, context.getDisplay(), inflationController);
+ KeyguardPresentation keyguardPresentation = new KeyguardPresentation(context,
+ context.getDisplayNoVerify(), inflationController);
keyguardPresentation.onCreate(null /*savedInstanceState */);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index befe3e1..6a093963 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -75,6 +76,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@SmallTest
@@ -117,6 +119,8 @@
private SubscriptionManager mSubscriptionManager;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private Executor mBackgroundExecutor;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -137,7 +141,9 @@
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
- when(mStrongAuthTracker.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mStrongAuthTracker
+ .isUnlockingWithBiometricAllowed(anyBoolean() /* isStrongBiometric */))
+ .thenReturn(true);
context.addMockSystemService(TrustManager.class, mTrustManager);
context.addMockSystemService(FingerprintManager.class, mFingerprintManager);
context.addMockSystemService(BiometricManager.class, mBiometricManager);
@@ -450,7 +456,10 @@
@Test
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
- mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
+ // test whether face will be skipped if authenticated, so the value of isStrongBiometric
+ // doesn't matter here
+ mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
+ true /* isStrongBiometric */);
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
mTestableLooper.processAllMessages();
@@ -460,18 +469,36 @@
@Test
public void testGetUserCanSkipBouncer_whenFace() {
int user = KeyguardUpdateMonitor.getCurrentUser();
- mKeyguardUpdateMonitor.onFaceAuthenticated(user);
+ mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isStrongBiometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@Test
+ public void testGetUserCanSkipBouncer_whenFace_nonStrongAndDisallowed() {
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
+ .thenReturn(false);
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isStrongBiometric */);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
+ }
+
+ @Test
public void testGetUserCanSkipBouncer_whenFingerprint() {
int user = KeyguardUpdateMonitor.getCurrentUser();
- mKeyguardUpdateMonitor.onFingerprintAuthenticated(user);
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isStrongBiometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@Test
+ public void testGetUserCanSkipBouncer_whenFingerprint_nonStrongAndDisallowed() {
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
+ .thenReturn(false);
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isStrongBiometric */);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
+ }
+
+ @Test
public void testGetUserCanSkipBouncer_whenTrust() {
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
@@ -585,7 +612,7 @@
protected TestableKeyguardUpdateMonitor(Context context) {
super(context,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
- mBroadcastDispatcher, mDumpController);
+ mBroadcastDispatcher, mDumpController, mBackgroundExecutor);
mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker;
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 3330d1e..6199181 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -93,6 +93,8 @@
mMockPluginManager, mMockColorExtractor, mMockContentResolver,
mMockCurrentUserObserable, mMockSettingsWrapper, mFakeDockManager);
+ mClockManager.addBuiltinClock(() -> new BubbleClockController(
+ getContext().getResources(), inflater, mMockColorExtractor));
mClockManager.addOnClockChangedListener(mMockListener1);
mClockManager.addOnClockChangedListener(mMockListener2);
reset(mMockListener1, mMockListener2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
new file mode 100644
index 0000000..e8e98b4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemui.qs.tiles;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.service.quicksettings.Tile;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.screenrecord.RecordingController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class ScreenRecordTileTest extends SysuiTestCase {
+
+ @Mock
+ private RecordingController mController;
+ @Mock
+ private QSTileHost mHost;
+
+ private TestableLooper mTestableLooper;
+ private ScreenRecordTile mTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+ mController = mDependency.injectMockDependency(RecordingController.class);
+
+ when(mHost.getContext()).thenReturn(mContext);
+
+ mTile = new ScreenRecordTile(mHost, mController);
+ }
+
+ // Test that the tile is inactive and labeled correctly when the controller is neither starting
+ // or recording, and that clicking on the tile in this state brings up the record prompt
+ @Test
+ public void testNotActive() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(false);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
+ assertTrue(mTile.getState().secondaryLabel.toString().equals(
+ mContext.getString(R.string.quick_settings_screen_record_start)));
+
+ mTile.handleClick();
+ verify(mController, times(1)).launchRecordPrompt();
+ }
+
+ // Test that the tile is active and labeled correctly when the controller is starting
+ @Test
+ public void testIsStarting() {
+ when(mController.isStarting()).thenReturn(true);
+ when(mController.isRecording()).thenReturn(false);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+ assertTrue(mTile.getState().secondaryLabel.toString().endsWith("..."));
+ }
+
+ // Test that the tile cancels countdown if it is clicked when the controller is starting
+ @Test
+ public void testCancelRecording() {
+ when(mController.isStarting()).thenReturn(true);
+ when(mController.isRecording()).thenReturn(false);
+
+ mTile.handleClick();
+
+ verify(mController, times(1)).cancelCountdown();
+ }
+
+ // Test that the tile is active and labeled correctly when the controller is recording
+ @Test
+ public void testIsRecording() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(true);
+
+ mTile.refreshState();
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
+ assertTrue(mTile.getState().secondaryLabel.toString().equals(
+ mContext.getString(R.string.quick_settings_screen_record_stop)));
+ }
+
+ // Test that the tile stops the recording if it is clicked when the controller is recording
+ @Test
+ public void testStopRecording() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(true);
+
+ mTile.handleClick();
+
+ verify(mController, times(1)).stopRecording();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
new file mode 100644
index 0000000..b877c7f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemui.screenrecord;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.PendingIntent;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+/**
+ * Tests for exception handling and bitmap configuration in adding smart actions to Screenshot
+ * Notification.
+ */
+public class RecordingControllerTest extends SysuiTestCase {
+
+ @Mock
+ RecordingController.RecordingStateChangeCallback mCallback;
+
+ RecordingController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new RecordingController(mContext);
+ mController.addCallback(mCallback);
+ }
+
+ // Test that when a countdown in progress is cancelled, the controller goes from starting to not
+ // starting, and notifies listeners.
+ @Test
+ public void testCancelCountdown() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ mController.startCountdown(100, 10, null, null);
+
+ assertTrue(mController.isStarting());
+ assertFalse(mController.isRecording());
+
+ mController.cancelCountdown();
+
+ assertFalse(mController.isStarting());
+ assertFalse(mController.isRecording());
+
+ verify(mCallback).onCountdownEnd();
+ }
+
+ // Test that when recording is started, the start intent is sent and listeners are notified.
+ @Test
+ public void testStartRecording() throws PendingIntent.CanceledException {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ PendingIntent startIntent = Mockito.mock(PendingIntent.class);
+ mController.startCountdown(0, 0, startIntent, null);
+
+ verify(mCallback).onCountdownEnd();
+ verify(startIntent).send();
+ }
+
+ // Test that when recording is stopped, the stop intent is sent and listeners are notified.
+ @Test
+ public void testStopRecording() throws PendingIntent.CanceledException {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
+ PendingIntent startIntent = Mockito.mock(PendingIntent.class);
+ PendingIntent stopIntent = Mockito.mock(PendingIntent.class);
+
+ mController.startCountdown(0, 0, startIntent, stopIntent);
+ mController.stopRecording();
+
+ assertFalse(mController.isStarting());
+ assertFalse(mController.isRecording());
+ verify(stopIntent).send();
+ verify(mCallback).onRecordingEnd();
+ }
+
+ // Test that updating the controller state works and notifies listeners.
+ @Test
+ public void testUpdateState() {
+ mController.updateState(true);
+ assertTrue(mController.isRecording());
+ verify(mCallback).onRecordingStart();
+
+ mController.updateState(false);
+ assertFalse(mController.isRecording());
+ verify(mCallback).onRecordingEnd();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java
deleted file mode 100644
index 0b871e4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotStubActivity.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.systemui.screenshot;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import com.android.systemui.tests.R;
-
-/**
- * A stub activity used in {@link ScreenshotTest}.
- */
-public class ScreenshotStubActivity extends Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 1d4b4be..581d795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -124,7 +125,7 @@
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
- when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index b3d0d22..6388fe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -63,7 +63,7 @@
DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false)
assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled())
- assertTrue("Expecting 3 buckets when people filtering is enabled",
- manager!!.getNumberOfBuckets() == 3)
+ assertTrue("Expecting 4 buckets when people filtering is enabled",
+ manager!!.getNumberOfBuckets() == 4)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 8e330c6..f11c42b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -42,6 +43,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -52,18 +54,20 @@
}
private lateinit var personNotificationIdentifier: PeopleNotificationIdentifier
private lateinit var rankingManager: TestableNotificationRankingManager
+ private lateinit var sectionsManager: NotificationSectionsFeatureManager
@Before
fun setup() {
personNotificationIdentifier =
mock(PeopleNotificationIdentifier::class.java)
+ sectionsManager = mock(NotificationSectionsFeatureManager::class.java)
rankingManager = TestableNotificationRankingManager(
lazyMedia,
mock(NotificationGroupManager::class.java),
mock(HeadsUpManager::class.java),
mock(NotificationFilter::class.java),
mock(NotificationEntryManagerLogger::class.java),
- mock(NotificationSectionsFeatureManager::class.java),
+ sectionsManager,
personNotificationIdentifier,
HighPriorityProvider(personNotificationIdentifier)
)
@@ -145,40 +149,90 @@
}
@Test
- fun testSort_importantPeople() {
+ fun testSort_headsUp_trumpsPeople() {
+ whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
val aN = Notification.Builder(mContext, "test")
.setStyle(Notification.MessagingStyle(""))
.build()
- val aC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
- aC.setConversationId("parent", "convo")
val a = NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.setPkg("pkg")
.setOpPkg("pkg")
.setTag("tag")
.setNotification(aN)
- .setChannel(aC)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
+ whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(true)
+ whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(true)
+
val bN = Notification.Builder(mContext, "test")
.setStyle(Notification.MessagingStyle(""))
.build()
- val bC = NotificationChannel("test", "", IMPORTANCE_DEFAULT)
- bC.setConversationId("parent", "convo")
- bC.setImportantConversation(true)
val b = NotificationEntryBuilder()
.setImportance(IMPORTANCE_HIGH)
.setPkg("pkg2")
.setOpPkg("pkg2")
.setTag("tag")
.setNotification(bN)
- .setChannel(bC)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
.setUser(mContext.getUser())
.setOverrideGroupKey("")
.build()
+ b.row = mock(ExpandableNotificationRow::class.java).also {
+ whenever(it.isHeadsUp).thenReturn(true)
+ }
+ whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(false)
+ whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(false)
+
+ assertEquals(listOf(b, a), rankingManager.updateRanking(null, listOf(a, b), "test"))
+ }
+
+ @Test
+ fun testSort_importantPeople() {
+ whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
+ val aN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val a = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+ whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(false)
+ whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(true)
+
+ val bN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val b = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
+ .setUser(mContext.getUser())
+ .setOverrideGroupKey("")
+ .build()
+ whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(false)
+ whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking))
+ .thenReturn(true)
assertEquals(
listOf(b, a),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index 867a9b9..abce8b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -43,7 +43,7 @@
@JvmField @Rule val mockito: MockitoRule = MockitoJUnit.rule()
- @Mock private lateinit var mockViewBoundary: PeopleHubSectionFooterViewBoundary
+ @Mock private lateinit var mockViewBoundary: PeopleHubViewBoundary
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Test
@@ -67,7 +67,7 @@
return mockSubscription
}
}
- val adapter = PeopleHubSectionFooterViewAdapterImpl(fakeFactoryDataSource)
+ val adapter = PeopleHubViewAdapterImpl(fakeFactoryDataSource)
adapter.bindView(mockViewBoundary)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 51f214d..abfbcd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -45,8 +45,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
-import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.people.PeopleHubViewAdapter;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
@@ -71,7 +70,7 @@
@Mock private ActivityStarterDelegate mActivityStarterDelegate;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
- @Mock private PeopleHubSectionFooterViewAdapter mPeopleHubAdapter;
+ @Mock private PeopleHubViewAdapter mPeopleHubAdapter;
@Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
@Mock private NotificationRowComponent mNotificationRowComponent;
@Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@@ -90,18 +89,8 @@
mStatusBarStateController,
mConfigurationController,
mPeopleHubAdapter,
- mSectionsFeatureManager,
- new NotificationRowComponent.Builder() {
- @Override
- public NotificationRowComponent.Builder activatableNotificationView(
- ActivatableNotificationView view) {
- return this;
- }
-
- @Override
- public NotificationRowComponent build() {
- return mNotificationRowComponent;
- }});
+ mSectionsFeatureManager
+ );
// Required in order for the header inflation to work properly
when(mNssl.generateLayoutParams(any(AttributeSet.class)))
.thenReturn(new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 769b774..813923d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -97,7 +97,8 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
+ when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
+ .thenReturn(true);
when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true);
mContext.addMockSystemService(PowerManager.class, mPowerManager);
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
@@ -112,11 +113,28 @@
@Test
public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */))
+ .thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFingerprint_nonStrongBioDisallowed_showBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
+ .thenReturn(false);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */);
+ verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
+ verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
+ anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@Test
@@ -124,44 +142,60 @@
reset(mUpdateMonitor);
reset(mStatusBarKeyguardViewManager);
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mDozeScrimController.isPulsing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mKeyguardViewMediator).onWakeAndUnlocking();
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
@Test
public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
}
@Test
public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER);
}
@Test
public void onBiometricAuthenticated_whenFace_dontDismissKeyguard() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_NONE);
}
@Test
@@ -169,13 +203,17 @@
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_UNLOCK_FADING);
}
@Test
@@ -184,9 +222,11 @@
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
// Wake up before showing the bouncer
verify(mStatusBarKeyguardViewManager, never()).showBouncer(eq(false));
@@ -202,9 +242,11 @@
reset(mUpdateMonitor);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
@@ -215,23 +257,30 @@
@Test
public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER);
}
@Test
public void onBiometricAuthenticated_whenBypassOnBouncer_dismissBouncer() {
reset(mKeyguardBypassController);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
+ when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
+ .thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
@@ -240,11 +289,13 @@
@Test
public void onBiometricAuthenticated_whenBypassOnBouncer_respectsCanPlaySubtleAnim() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
@@ -255,13 +306,17 @@
public void onBiometricAuthenticated_whenFaceAndPulsing_dontDismissKeyguard() {
reset(mUpdateMonitor);
reset(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mDozeScrimController.isPulsing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_ONLY_WAKE);
}
@Test
@@ -270,8 +325,10 @@
mBiometricUnlockController.onFinishedGoingToSleep(-1);
verify(mHandler, never()).post(any());
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(1 /* userId */,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
mBiometricUnlockController.onFinishedGoingToSleep(-1);
verify(mHandler).post(any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 2e6fbe7..408dfc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -36,7 +36,6 @@
import android.animation.Animator;
import android.app.AlarmManager;
-import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
@@ -91,8 +90,6 @@
@Mock
LightBarController mLightBarController;
@Mock
- Resources mResources;
- @Mock
DelayedWakeLock.Builder mDelayedWakeLockBuilder;
@Mock
private DelayedWakeLock mWakeLock;
@@ -216,8 +213,7 @@
when(mDockManager.isDocked()).thenReturn(false);
mScrimController = new ScrimController(mLightBarController,
- mDozeParamenters, mAlarmManager, mKeyguardStateController,
- mResources, mDelayedWakeLockBuilder,
+ mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor,
mDockManager);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
new file mode 100644
index 0000000..f1672b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2020 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 com.android.systemui.util.magnetictarget
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.MotionEvent
+import android.view.View
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.Mockito
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class MagnetizedObjectTest : SysuiTestCase() {
+ /** Incrementing value for fake MotionEvent timestamps. */
+ private var time = 0L
+
+ /** Value to add to each new MotionEvent's timestamp. */
+ private var timeStep = 100
+
+ private val underlyingObject = this
+
+ private lateinit var targetView: View
+
+ private val targetSize = 200
+ private val targetCenterX = 500
+ private val targetCenterY = 900
+ private val magneticFieldRadius = 200
+
+ private var objectX = 0f
+ private var objectY = 0f
+ private val objectSize = 50f
+
+ private lateinit var magneticTarget: MagnetizedObject.MagneticTarget
+ private lateinit var magnetizedObject: MagnetizedObject<*>
+ private lateinit var magnetListener: MagnetizedObject.MagnetListener
+
+ private val xProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
+ override fun setValue(target: MagnetizedObjectTest?, value: Float) {
+ objectX = value
+ }
+ override fun getValue(target: MagnetizedObjectTest?): Float {
+ return objectX
+ }
+ }
+
+ private val yProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
+ override fun setValue(target: MagnetizedObjectTest?, value: Float) {
+ objectY = value
+ }
+
+ override fun getValue(target: MagnetizedObjectTest?): Float {
+ return objectY
+ }
+ }
+
+ @Before
+ fun setup() {
+ PhysicsAnimatorTestUtils.prepareForTest()
+
+ // Mock the view since a real view's getLocationOnScreen() won't work unless it's attached
+ // to a real window (it'll always return x = 0, y = 0).
+ targetView = mock(View::class.java)
+ `when`(targetView.context).thenReturn(context)
+
+ // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's
+ // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900).
+ `when`(targetView.width).thenReturn(targetSize) // width = 200
+ `when`(targetView.height).thenReturn(targetSize) // height = 200
+ doAnswer { invocation ->
+ (invocation.arguments[0] as IntArray).also { location ->
+ // Return the top left of the target.
+ location[0] = targetCenterX - targetSize / 2 // x = 400
+ location[1] = targetCenterY - targetSize / 2 // y = 800
+ }
+ }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any())
+ `when`(targetView.context).thenReturn(context)
+
+ magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius)
+
+ magnetListener = mock(MagnetizedObject.MagnetListener::class.java)
+ magnetizedObject = object : MagnetizedObject<MagnetizedObjectTest>(
+ context, underlyingObject, xProperty, yProperty) {
+ override fun getWidth(underlyingObject: MagnetizedObjectTest): Float {
+ return objectSize
+ }
+
+ override fun getHeight(underlyingObject: MagnetizedObjectTest): Float {
+ return objectSize
+ }
+
+ override fun getLocationOnScreen(
+ underlyingObject: MagnetizedObjectTest,
+ loc: IntArray
+ ) {
+ loc[0] = objectX.toInt()
+ loc[1] = objectY.toInt() }
+ }
+
+ magnetizedObject.magnetListener = magnetListener
+ magnetizedObject.addTarget(magneticTarget)
+
+ timeStep = 100
+ }
+
+ @Test
+ fun testMotionEventConsumption() {
+ // Start at (0, 0). No magnetic field here.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = 0, y = 0, action = MotionEvent.ACTION_DOWN)))
+
+ // Move to (400, 400), which is solidly outside the magnetic field.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = 200, y = 200)))
+
+ // Move to (305, 705). This would be in the magnetic field radius if magnetic fields were
+ // square. It's not, because they're not.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = targetCenterX - magneticFieldRadius + 5,
+ y = targetCenterY - magneticFieldRadius + 5)))
+
+ // Move to (400, 800). That's solidly in the radius so the magnetic target should begin
+ // consuming events.
+ assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = targetCenterX - 100,
+ y = targetCenterY - 100)))
+
+ // Release at (400, 800). Since we're in the magnetic target, it should return true and
+ // consume the ACTION_UP.
+ assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = 400, y = 800, action = MotionEvent.ACTION_UP)))
+
+ // ACTION_DOWN outside the field.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = 200, y = 200, action = MotionEvent.ACTION_DOWN)))
+
+ // Move to the center. We absolutely should consume events there.
+ assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY)))
+
+ // Drag out to (0, 0) and we should be returning false again.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = 0, y = 0)))
+
+ // The ACTION_UP event shouldn't be consumed either since it's outside the field.
+ assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = 0, y = 0, action = MotionEvent.ACTION_UP)))
+ }
+
+ @Test
+ fun testMotionEventConsumption_downInMagneticField() {
+ // We should consume DOWN events if they occur in the field.
+ assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN)))
+ }
+
+ @Test
+ fun testMoveIntoAroundAndOutOfMagneticField() {
+ // Move around but don't touch the magnetic field.
+ dispatchMotionEvents(
+ getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(x = 100, y = 100),
+ getMotionEvent(x = 200, y = 200))
+
+ // You can't become unstuck if you were never stuck in the first place.
+ verify(magnetListener, never()).onStuckToTarget(magneticTarget)
+ verify(magnetListener, never()).onUnstuckFromTarget(
+ eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+ eq(false))
+
+ // Move into and then around inside the magnetic field.
+ dispatchMotionEvents(
+ getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
+ getMotionEvent(x = targetCenterX, y = targetCenterY),
+ getMotionEvent(x = targetCenterX + 100, y = targetCenterY + 100))
+
+ // We should only have received one call to onStuckToTarget and none to unstuck.
+ verify(magnetListener, times(1)).onStuckToTarget(magneticTarget)
+ verify(magnetListener, never()).onUnstuckFromTarget(
+ eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+ eq(false))
+
+ // Move out of the field and then release.
+ dispatchMotionEvents(
+ getMotionEvent(x = 100, y = 100),
+ getMotionEvent(x = 100, y = 100, action = MotionEvent.ACTION_UP))
+
+ // We should have received one unstuck call and no more stuck calls. We also should never
+ // have received an onReleasedInTarget call.
+ verify(magnetListener, times(1)).onUnstuckFromTarget(
+ eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+ eq(false))
+ verifyNoMoreInteractions(magnetListener)
+ }
+
+ @Test
+ fun testMoveIntoOutOfAndBackIntoMagneticField() {
+ // Move into the field
+ dispatchMotionEvents(
+ getMotionEvent(
+ x = targetCenterX - magneticFieldRadius,
+ y = targetCenterY - magneticFieldRadius,
+ action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(
+ x = targetCenterX, y = targetCenterY))
+
+ verify(magnetListener, times(1)).onStuckToTarget(magneticTarget)
+ verify(magnetListener, never()).onReleasedInTarget(magneticTarget)
+
+ // Move back out.
+ dispatchMotionEvents(
+ getMotionEvent(
+ x = targetCenterX - magneticFieldRadius,
+ y = targetCenterY - magneticFieldRadius))
+
+ verify(magnetListener, times(1)).onUnstuckFromTarget(
+ eq(magneticTarget), ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+ eq(false))
+ verify(magnetListener, never()).onReleasedInTarget(magneticTarget)
+
+ // Move in again and release in the magnetic field.
+ dispatchMotionEvents(
+ getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
+ getMotionEvent(x = targetCenterX + 50, y = targetCenterY + 50),
+ getMotionEvent(x = targetCenterX, y = targetCenterY),
+ getMotionEvent(
+ x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_UP))
+
+ verify(magnetListener, times(2)).onStuckToTarget(magneticTarget)
+ verify(magnetListener).onReleasedInTarget(magneticTarget)
+ verifyNoMoreInteractions(magnetListener)
+ }
+
+ @Test
+ fun testFlingTowardsTarget_towardsTarget() {
+ timeStep = 10
+
+ // Forcefully fling the object towards the target (but never touch the magnetic field).
+ dispatchMotionEvents(
+ getMotionEvent(
+ x = targetCenterX,
+ y = 0,
+ action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY / 2),
+ getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY - magneticFieldRadius * 2,
+ action = MotionEvent.ACTION_UP))
+
+ // Nevertheless it should have ended up stuck to the target.
+ verify(magnetListener, times(1)).onStuckToTarget(magneticTarget)
+ }
+
+ @Test
+ fun testFlingTowardsTarget_towardsButTooSlow() {
+ // Very, very slowly fling the object towards the target (but never touch the magnetic
+ // field). This value is only used to create MotionEvent timestamps, it will not block the
+ // test for 10 seconds.
+ timeStep = 10000
+ dispatchMotionEvents(
+ getMotionEvent(
+ x = targetCenterX,
+ y = 0,
+ action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY / 2),
+ getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY - magneticFieldRadius * 2,
+ action = MotionEvent.ACTION_UP))
+
+ // No sticking should have occurred.
+ verifyNoMoreInteractions(magnetListener)
+ }
+
+ @Test
+ fun testFlingTowardsTarget_missTarget() {
+ timeStep = 10
+ // Forcefully fling the object down, but not towards the target.
+ dispatchMotionEvents(
+ getMotionEvent(
+ x = 0,
+ y = 0,
+ action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(
+ x = 0,
+ y = targetCenterY / 2),
+ getMotionEvent(
+ x = 0,
+ y = targetCenterY - magneticFieldRadius * 2,
+ action = MotionEvent.ACTION_UP))
+
+ verifyNoMoreInteractions(magnetListener)
+ }
+
+ @Test
+ fun testMagnetAnimation() {
+ // Make sure the object starts at (0, 0).
+ assertEquals(0f, objectX)
+ assertEquals(0f, objectY)
+
+ // Trigger the magnet animation, and block the test until it ends.
+ PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
+ magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+ x = targetCenterX,
+ y = targetCenterY,
+ action = MotionEvent.ACTION_DOWN))
+
+ // The object's (top-left) position should now position it centered over the target.
+ assertEquals(targetCenterX - objectSize / 2, objectX)
+ assertEquals(targetCenterY - objectSize / 2, objectY)
+ }
+
+ @Test
+ fun testMultipleTargets() {
+ val secondMagneticTarget = getSecondMagneticTarget()
+
+ // Drag into the second target.
+ dispatchMotionEvents(
+ getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(x = 100, y = 900))
+
+ // Verify that we received an onStuck for the second target, and no others.
+ verify(magnetListener).onStuckToTarget(secondMagneticTarget)
+ verifyNoMoreInteractions(magnetListener)
+
+ // Drag into the original target.
+ dispatchMotionEvents(
+ getMotionEvent(x = 0, y = 0),
+ getMotionEvent(x = 500, y = 900))
+
+ // We should have unstuck from the second one and stuck into the original one.
+ verify(magnetListener).onUnstuckFromTarget(
+ eq(secondMagneticTarget), anyFloat(), anyFloat(), eq(false))
+ verify(magnetListener).onStuckToTarget(magneticTarget)
+ verifyNoMoreInteractions(magnetListener)
+ }
+
+ @Test
+ fun testMultipleTargets_flingIntoSecond() {
+ val secondMagneticTarget = getSecondMagneticTarget()
+
+ timeStep = 10
+
+ // Fling towards the second target.
+ dispatchMotionEvents(
+ getMotionEvent(x = 100, y = 0, action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(x = 100, y = 350),
+ getMotionEvent(x = 100, y = 650, action = MotionEvent.ACTION_UP))
+
+ // Verify that we received an onStuck for the second target.
+ verify(magnetListener).onStuckToTarget(secondMagneticTarget)
+
+ // Fling towards the first target.
+ dispatchMotionEvents(
+ getMotionEvent(x = 300, y = 0, action = MotionEvent.ACTION_DOWN),
+ getMotionEvent(x = 400, y = 350),
+ getMotionEvent(x = 500, y = 650, action = MotionEvent.ACTION_UP))
+
+ // Verify that we received onStuck for the original target.
+ verify(magnetListener).onStuckToTarget(magneticTarget)
+ }
+
+ private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget {
+ // The first target view is at bounds (400, 800, 600, 1000) and it has a center of
+ // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900).
+ val secondTargetView = mock(View::class.java)
+ var secondTargetCenterX = 100
+ var secondTargetCenterY = 900
+
+ `when`(secondTargetView.context).thenReturn(context)
+ `when`(secondTargetView.width).thenReturn(targetSize) // width = 200
+ `when`(secondTargetView.height).thenReturn(targetSize) // height = 200
+ doAnswer { invocation ->
+ (invocation.arguments[0] as IntArray).also { location ->
+ // Return the top left of the target.
+ location[0] = secondTargetCenterX - targetSize / 2 // x = 0
+ location[1] = secondTargetCenterY - targetSize / 2 // y = 800
+ }
+ }.`when`(secondTargetView).getLocationOnScreen(ArgumentMatchers.any())
+
+ return magnetizedObject.addTarget(secondTargetView, magneticFieldRadius)
+ }
+
+ /**
+ * Return a MotionEvent at the given coordinates, with the given action (or MOVE by default).
+ * The event's time fields will be incremented by 10ms each time this is called, so tha
+ * VelocityTracker works.
+ */
+ private fun getMotionEvent(
+ x: Int,
+ y: Int,
+ action: Int = MotionEvent.ACTION_MOVE
+ ): MotionEvent {
+ return MotionEvent.obtain(time, time, action, x.toFloat(), y.toFloat(), 0)
+ .also { time += timeStep }
+ }
+
+ /** Dispatch all of the provided events to the target view. */
+ private fun dispatchMotionEvents(vararg events: MotionEvent) {
+ events.forEach { magnetizedObject.maybeConsumeMotionEvent(it) }
+ }
+
+ /** Prevents Kotlin from being mad that eq() is nullable. */
+ private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+}
\ No newline at end of file
diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING
new file mode 100644
index 0000000..73254cd
--- /dev/null
+++ b/packages/Tethering/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "TetheringTests"
+ }
+ ]
+}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index cb0de7a..1a3d5b6 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -83,7 +83,6 @@
name: "framework-tethering-stubs",
srcs: [":framework-tethering-stubs-sources"],
libs: ["framework-all"],
- static_libs: ["tethering-aidl-interfaces-java"],
sdk_version: "core_platform",
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index df87ac9..a18f5da 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -33,6 +33,9 @@
*/
@SystemApi(client = MODULE_LIBRARIES)
public class TetheringConstants {
+ /** An explicit private class to avoid exposing constructor.*/
+ private TetheringConstants() { }
+
/**
* Extra used for communicating with the TetherService. Includes the type of tethering to
* enable if any.
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index b4d49c0..3acc766 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -40,12 +40,14 @@
import android.net.dhcp.DhcpServingParamsParcelExt;
import android.net.dhcp.IDhcpLeaseCallbacks;
import android.net.dhcp.IDhcpServer;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.shared.NetdUtils;
import android.net.shared.RouteUtils;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
+import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
@@ -59,14 +61,17 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Random;
@@ -149,6 +154,12 @@
/** Capture IpServer dependencies, for injection. */
public abstract static class Dependencies {
+ /** Create an IpNeighborMonitor to be used by this IpServer */
+ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
+ IpNeighborMonitor.NeighborEventConsumer consumer) {
+ return new IpNeighborMonitor(handler, log, consumer);
+ }
+
/** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
@@ -159,6 +170,15 @@
return InterfaceParams.getByName(ifName);
}
+ /** Get |ifName|'s interface index. */
+ public int getIfindex(String ifName) {
+ try {
+ return NetworkInterface.getByName(ifName).getIndex();
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Can't determine interface index for interface " + ifName);
+ return 0;
+ }
+ }
/** Create a DhcpServer instance to be used by IpServer. */
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb);
@@ -184,6 +204,8 @@
public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9;
// new IPv6 tethering parameters need to be processed
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
+ // new neighbor cache entry on our interface
+ public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
private final State mInitialState;
private final State mLocalHotspotState;
@@ -223,6 +245,40 @@
@NonNull
private List<TetheredClient> mDhcpLeases = Collections.emptyList();
+ private int mLastIPv6UpstreamIfindex = 0;
+
+ private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
+ public void accept(NeighborEvent e) {
+ sendMessage(CMD_NEIGHBOR_EVENT, e);
+ }
+ }
+
+ static class Ipv6ForwardingRule {
+ public final int upstreamIfindex;
+ public final int downstreamIfindex;
+ public final Inet6Address address;
+ public final MacAddress srcMac;
+ public final MacAddress dstMac;
+
+ Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
+ MacAddress srcMac, MacAddress dstMac) {
+ this.upstreamIfindex = upstreamIfindex;
+ this.downstreamIfindex = downstreamIfIndex;
+ this.address = address;
+ this.srcMac = srcMac;
+ this.dstMac = dstMac;
+ }
+
+ public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
+ return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
+ dstMac);
+ }
+ }
+ private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
+ new LinkedHashMap<>();
+
+ private final IpNeighborMonitor mIpNeighborMonitor;
+
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
@@ -240,6 +296,12 @@
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE;
+ mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
+ new MyNeighborEventConsumer());
+ if (!mIpNeighborMonitor.start()) {
+ mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
+ }
+
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
mTetheredState = new TetheredState();
@@ -607,13 +669,21 @@
}
RaParams params = null;
+ int upstreamIfindex = 0;
if (v6only != null) {
+ final String upstreamIface = v6only.getInterfaceName();
+
params = new RaParams();
- params.mtu = v6only.getMtu();
+ // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14,
+ // the ethernet header size. This makes kernel ebpf tethering offload happy.
+ // This hack should be reverted once we have the kernel fixed up.
+ // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
+ // see RouterAdvertisementDaemon.java putMtu()
+ params.mtu = v6only.getMtu() - 16;
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
- if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName());
+ if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -627,12 +697,18 @@
params.dnses.add(dnsServer);
}
}
+
+ upstreamIfindex = mDeps.getIfindex(upstreamIface);
}
+
// If v6only is null, we pass in null to setRaParams(), which handles
// deprecation of any existing RA data.
setRaParams(params);
mLastIPv6LinkProperties = v6only;
+
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null);
+ mLastIPv6UpstreamIfindex = upstreamIfindex;
}
private void configureLocalIPv6Routes(
@@ -727,6 +803,73 @@
}
}
+ private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
+ try {
+ mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
+ rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(),
+ rule.dstMac.toByteArray());
+ mIpv6ForwardingRules.put(rule.address, rule);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Could not add IPv6 downstream rule: " + e);
+ }
+ }
+
+ private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
+ try {
+ mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
+ if (removeFromMap) {
+ mIpv6ForwardingRules.remove(rule.address);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Could not remove IPv6 downstream rule: " + e);
+ }
+ }
+
+ // Convenience method to replace a rule with the same rule on a new upstream interface.
+ // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
+ // Relies on the fact that rules are in a map indexed by IP address.
+ private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) {
+ addIpv6ForwardingRule(rule.onNewUpstream(newIfindex));
+ removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+ }
+
+ // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
+ // changes or if a neighbor event is received.
+ private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
+ NeighborEvent e) {
+ // If the upstream interface has changed, remove all rules and re-add them with the new
+ // upstream interface.
+ if (prevUpstreamIfindex != upstreamIfindex) {
+ for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
+ updateIpv6ForwardingRule(rule, upstreamIfindex);
+ }
+ }
+
+ // If we're here to process a NeighborEvent, do so now.
+ if (e == null) return;
+ if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
+ || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
+ return;
+ }
+
+ Ipv6ForwardingRule rule = new Ipv6ForwardingRule(mLastIPv6UpstreamIfindex,
+ mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
+ e.macAddr);
+ if (e.isValid()) {
+ addIpv6ForwardingRule(rule);
+ } else {
+ removeIpv6ForwardingRule(rule, true /*removeFromMap*/);
+ }
+ }
+
+ private void handleNeighborEvent(NeighborEvent e) {
+ if (mInterfaceParams != null
+ && mInterfaceParams.index == e.ifindex
+ && mInterfaceParams.hasMacAddress) {
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
+ }
+ }
+
private byte getHopLimit(String upstreamIface) {
try {
int upstreamHopLimit = Integer.parseUnsignedInt(
@@ -1014,6 +1157,9 @@
}
}
break;
+ case CMD_NEIGHBOR_EVENT:
+ handleNeighborEvent((NeighborEvent) message.obj);
+ break;
default:
return false;
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 6261def..72fe95b 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -314,9 +314,13 @@
startStateMachineUpdaters(mHandler);
startTrackDefaultNetwork();
- getWifiManager().registerSoftApCallback(
- mHandler::post /* executor */,
- new TetheringSoftApCallback());
+
+ final WifiManager wifiManager = getWifiManager();
+ if (wifiManager != null) {
+ wifiManager.registerSoftApCallback(
+ mHandler::post /* executor */,
+ new TetheringSoftApCallback());
+ }
}
private void startStateMachineUpdaters(Handler handler) {
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index acedfab..33b3558 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -29,6 +29,11 @@
import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH;
+import static android.net.netlink.StructNdMsg.NUD_FAILED;
+import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
+import static android.net.netlink.StructNdMsg.NUD_STALE;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static org.junit.Assert.assertEquals;
@@ -41,6 +46,7 @@
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -52,6 +58,7 @@
import static org.mockito.Mockito.when;
import android.net.INetd;
+import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -61,6 +68,8 @@
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
+import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -81,6 +90,7 @@
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
+import java.net.InetAddress;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -88,6 +98,8 @@
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
+ private static final int UPSTREAM_IFINDEX = 101;
+ private static final int UPSTREAM_IFINDEX2 = 102;
private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -102,6 +114,7 @@
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
+ @Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -111,6 +124,7 @@
ArgumentCaptor.forClass(LinkProperties.class);
private IpServer mIpServer;
private InterfaceConfigurationParcel mInterfaceConfiguration;
+ private NeighborEventConsumer mNeighborEventConsumer;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */);
@@ -130,16 +144,28 @@
}).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+
+ when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
+ when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
+
mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0];
if (interfaceType == TETHERING_BLUETOOTH) {
mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
}
+
+ ArgumentCaptor<NeighborEventConsumer> neighborCaptor =
+ ArgumentCaptor.forClass(NeighborEventConsumer.class);
+ doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
+ neighborCaptor.capture());
+
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
mCallback, usingLegacyDhcp, mDependencies);
mIpServer.start();
+ mNeighborEventConsumer = neighborCaptor.getValue();
+
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
@@ -158,7 +184,9 @@
initStateMachine(interfaceType, usingLegacyDhcp);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
- dispatchTetherConnectionChanged(upstreamIface);
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(upstreamIface);
+ dispatchTetherConnectionChanged(upstreamIface, lp);
}
reset(mNetd, mCallback);
}
@@ -170,6 +198,8 @@
@Test
public void startsOutAvailable() {
+ when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
+ .thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
mIpServer.start();
@@ -467,6 +497,89 @@
verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
}
+ private InetAddress addr(String addr) throws Exception {
+ return InetAddresses.parseNumericAddress(addr);
+ }
+
+ private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+ mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
+ nudState, mac));
+ mLooper.dispatchAll();
+ }
+
+ private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+ mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
+ nudState, mac));
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void addRemoveipv6ForwardingRules() throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
+
+ final int myIfindex = TEST_IFACE_PARAMS.index;
+ final int notMyIfindex = myIfindex - 1;
+
+ final MacAddress myMac = TEST_IFACE_PARAMS.macAddr;
+ final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
+ final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
+ final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
+ final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
+ final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+ final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
+
+ reset(mNetd);
+
+ // Events on other interfaces are ignored.
+ recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+
+ // Events on this interface are received and sent to netd.
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+ reset(mNetd);
+
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+ reset(mNetd);
+
+ // Link-local and multicast neighbors are ignored.
+ recvNewNeigh(notMyIfindex, neighLL, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+ recvNewNeigh(notMyIfindex, neighMC, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+
+ // A neighbor that is no longer valid causes the rule to be removed.
+ recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA);
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+ reset(mNetd);
+
+ // A neighbor that is deleted causes the rule to be removed.
+ recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+ reset(mNetd);
+
+ // Upstream changes result in deleting and re-adding the rules.
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ reset(mNetd);
+
+ InOrder inOrder = inOrder(mNetd);
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE2);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
+ inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
+ eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+ inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
+ eq(neighA.getAddress()));
+ inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
+ eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+ inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
+ eq(neighB.getAddress()));
+ }
+
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
@@ -508,13 +621,21 @@
*
* @see #dispatchCommand(int)
* @param upstreamIface String name of upstream interface (or null)
+ * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
*/
- private void dispatchTetherConnectionChanged(String upstreamIface) {
+ private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) {
mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
new InterfaceSet(upstreamIface));
+ if (v6lp != null) {
+ mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
+ }
mLooper.dispatchAll();
}
+ private void dispatchTetherConnectionChanged(String upstreamIface) {
+ dispatchTetherConnectionChanged(upstreamIface, null);
+ }
+
private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
// Find the first IPv4 LinkAddress.
LinkAddress addr4 = null;
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 8e5aaf2..f2074bd 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -95,6 +95,7 @@
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
+import android.net.ip.IpNeighborMonitor;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
@@ -173,6 +174,7 @@
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+ @Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd;
@Mock private UserManager mUserManager;
@@ -278,6 +280,11 @@
}
}).run();
}
+
+ public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l,
+ IpNeighborMonitor.NeighborEventConsumer c) {
+ return mIpNeighborMonitor;
+ }
}
private class MockTetheringConfiguration extends TetheringConfiguration {
diff --git a/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java b/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java
index fcb113e..210fdc6 100644
--- a/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java
+++ b/packages/WallpaperCropper/src/com/android/photos/views/TiledImageRenderer.java
@@ -163,7 +163,7 @@
private static boolean isHighResolution(Context context) {
DisplayMetrics metrics = new DisplayMetrics();
- context.getDisplay().getMetrics(metrics);
+ context.getDisplayNoVerify().getMetrics(metrics);
return metrics.heightPixels > 2048 || metrics.widthPixels > 2048;
}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index eda3fb9..cff5504 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -252,6 +252,9 @@
// Package: android
NOTE_ID_WIFI_SIM_REQUIRED = 60;
+ // Inform the user a foreground service while-in-use permission is restricted.
+ NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION = 61;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index c77e75d..db6e21a 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -114,9 +114,10 @@
name: "services-stubs.sources",
srcs: [":services-all-sources"],
installable: false,
- // TODO: remove the --hide options below
args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
" --hide-annotation android.annotation.Hide" +
+ " --hide InternalClasses" + // com.android.* classes are okay in this interface
+ // TODO: remove the --hide options below
" --hide-package com.google.android.startop.iorap" +
" --hide ReferencesHidden" +
" --hide DeprecationMismatch" +
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
index 265674a..c6f42f7 100644
--- a/services/accessibility/OWNERS
+++ b/services/accessibility/OWNERS
@@ -1,3 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
+qasid@google.com
diff --git a/services/api/current.txt b/services/api/current.txt
index 8a82e61..26a65f2 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -3,9 +3,9 @@
public interface RuntimePermissionsPersistence {
method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
- method public void delete(@NonNull android.os.UserHandle);
- method @Nullable public com.android.permission.persistence.RuntimePermissionsState read(@NonNull android.os.UserHandle);
- method public void write(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
+ method public void deleteAsUser(@NonNull android.os.UserHandle);
+ method @Nullable public com.android.permission.persistence.RuntimePermissionsState readAsUser(@NonNull android.os.UserHandle);
+ method public void writeAsUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
}
public final class RuntimePermissionsState {
@@ -30,9 +30,9 @@
public interface RolesPersistence {
method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
- method public void delete(@NonNull android.os.UserHandle);
- method @Nullable public com.android.role.persistence.RolesState read(@NonNull android.os.UserHandle);
- method public void write(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
+ method public void deleteAsUser(@NonNull android.os.UserHandle);
+ method @Nullable public com.android.role.persistence.RolesState readAsUser(@NonNull android.os.UserHandle);
+ method public void writeAsUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
}
public final class RolesState {
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
index 0b8658c..e985ddb 100644
--- a/services/api/lint-baseline.txt
+++ b/services/api/lint-baseline.txt
@@ -1,35 +1,5 @@
// Baseline format: 1.0
-InternalClasses: com.android.permission.persistence.RuntimePermissionsPersistence:
- Internal classes must not be exposed
-InternalClasses: com.android.permission.persistence.RuntimePermissionsState:
- Internal classes must not be exposed
-InternalClasses: com.android.permission.persistence.RuntimePermissionsState.PermissionState:
- Internal classes must not be exposed
-InternalClasses: com.android.role.persistence.RolesPersistence:
- Internal classes must not be exposed
-InternalClasses: com.android.role.persistence.RolesState:
- Internal classes must not be exposed
-InternalClasses: com.android.server.SystemService:
- Internal classes must not be exposed
-InternalClasses: com.android.server.SystemService.TargetUser:
- Internal classes must not be exposed
-
-
ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
-
-
-UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#delete(android.os.UserHandle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
-UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#read(android.os.UserHandle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
-UserHandleName: com.android.permission.persistence.RuntimePermissionsPersistence#write(com.android.permission.persistence.RuntimePermissionsState, android.os.UserHandle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
-UserHandleName: com.android.role.persistence.RolesPersistence#delete(android.os.UserHandle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `delete`
-UserHandleName: com.android.role.persistence.RolesPersistence#read(android.os.UserHandle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `read`
-UserHandleName: com.android.role.persistence.RolesPersistence#write(com.android.role.persistence.RolesState, android.os.UserHandle):
- Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `write`
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 7151d2b..a8a2791 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -281,7 +281,7 @@
}
private void computeMaximumWidgetBitmapMemory() {
- Display display = mContext.getDisplay();
+ Display display = mContext.getDisplayNoVerify();
Point size = new Point();
display.getRealSize(size);
// Cap memory usage at 1.5 times the size of the display
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 1fc48d2..6daa106 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import android.view.autofill.AutofillId;
@@ -29,6 +30,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.util.concurrent.CancellationException;
@@ -71,8 +73,10 @@
synchronized (mLock) {
cancelCurrentRequest();
mPendingImeResponse = new CompletableFuture<>();
+ // TODO(b/146454892): pipe the uiExtras from the ExtServices.
mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
- mUserId, mComponentName, currentViewId,
+ mUserId,
+ new InlineSuggestionsRequestInfo(mComponentName, currentViewId, new Bundle()),
new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f544517..317ce4c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1158,6 +1158,18 @@
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to hide fill UI", e);
}
+ try {
+ final InlineSuggestionSession.ImeResponse imeResponse =
+ mInlineSuggestionSession.waitAndGetImeResponse();
+ if (imeResponse == null) {
+ Log.w(TAG, "Session input method callback is not set yet");
+ return;
+ }
+ imeResponse.getCallback().onInlineSuggestionsResponse(
+ new InlineSuggestionsResponse(Collections.EMPTY_LIST));
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException hiding inline suggestions");
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
index 813fc8d..14bd7d7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
+++ b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java
@@ -75,7 +75,7 @@
final TypedValue typedValue = new TypedValue();
final Point point = new Point();
final Context context = getContext();
- context.getDisplay().getSize(point);
+ context.getDisplayNoVerify().getSize(point);
context.getTheme().resolveAttribute(R.attr.autofillSaveCustomSubtitleMaxHeight,
typedValue, true);
final View child = getChildAt(0);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 5dc43ef..344b92f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -165,7 +165,7 @@
// In full screen we only initialize size once assuming screen size never changes
if (mFullScreen) {
final Point outPoint = mTempPoint;
- mContext.getDisplay().getSize(outPoint);
+ mContext.getDisplayNoVerify().getSize(outPoint);
// full with of screen and half height of screen
mContentWidth = LayoutParams.MATCH_PARENT;
mContentHeight = outPoint.y / 2;
@@ -559,7 +559,7 @@
}
private static void resolveMaxWindowSize(Context context, Point outPoint) {
- context.getDisplay().getSize(outPoint);
+ context.getDisplayNoVerify().getSize(outPoint);
final TypedValue typedValue = sTempTypedValue;
context.getTheme().resolveAttribute(R.attr.autofillDatasetPickerMaxWidth,
typedValue, true);
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index a8886fc..17cdf61 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -82,12 +82,17 @@
if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
final BiConsumer<Dataset, Integer> onClickFactory;
if (response.getAuthentication() != null) {
- onClickFactory = (dataset, datasetIndex) -> client.authenticate(response.getRequestId(),
- datasetIndex, response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ true);
+ onClickFactory = (dataset, datasetIndex) -> {
+ client.requestHideFillUi(autofillId);
+ client.authenticate(response.getRequestId(),
+ datasetIndex, response.getAuthentication(), response.getClientState(),
+ /* authenticateInline= */ true);
+ };
} else {
- onClickFactory = (dataset, datasetIndex) ->
- client.fill(response.getRequestId(), datasetIndex, dataset);
+ onClickFactory = (dataset, datasetIndex) -> {
+ client.requestHideFillUi(autofillId);
+ client.fill(response.getRequestId(), datasetIndex, dataset);
+ };
}
final List<Dataset> datasetList = response.getDatasets();
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 3c37f73..e434be6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -76,9 +76,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -802,6 +800,7 @@
final int size = in.getDataSize();
if (excludedKeysForPackage != null && excludedKeysForPackage.contains(key)) {
+ Slog.i(TAG, "Skipping blocked key " + key);
in.skipEntityData();
continue;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6fc6084..228d9be 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -113,6 +113,7 @@
"android.hardware.broadcastradio-V2.0-java",
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
+ "android.hardware.health-V2.1-java",
"android.hardware.light-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.1-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 76f6ef6..0f8d57e 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -29,8 +29,7 @@
import android.content.pm.PackageManager.ComponentInfoFlags;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedMainComponent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.ArrayMap;
@@ -38,6 +37,8 @@
import android.util.SparseArray;
import com.android.server.pm.PackageList;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -586,9 +587,7 @@
*/
public abstract @Nullable AndroidPackage getPackage(@NonNull String packageName);
- // TODO(b/135203078): PackageSetting can't be referenced directly. Should move to a server side
- // internal PM which is aware of PS.
- public abstract @Nullable Object getPackageSetting(String packageName);
+ public abstract @Nullable PackageSetting getPackageSetting(String packageName);
/**
* Returns a package for the given UID. If the UID is part of a shared user ID, one
@@ -625,18 +624,17 @@
*/
public abstract void removePackageListObserver(@NonNull PackageListObserver observer);
- // TODO(b/135203078): PackageSetting can't be referenced directly
/**
* Returns a package object for the disabled system package name.
*/
- public abstract @Nullable Object getDisabledSystemPackage(@NonNull String packageName);
+ public abstract @Nullable PackageSetting getDisabledSystemPackage(@NonNull String packageName);
/**
* Returns the package name for the disabled system package.
*
* This is equivalent to
* {@link #getDisabledSystemPackage(String)}
- * .{@link com.android.server.pm.PackageSetting#pkg}
+ * .{@link PackageSetting#pkg}
* .{@link AndroidPackage#getPackageName()}
*/
public abstract @Nullable String getDisabledSystemPackageName(@NonNull String packageName);
@@ -677,7 +675,7 @@
/**
* Returns whether or not access to the application should be filtered.
*
- * @see #filterAppAccess(android.content.pm.PackageParser.Package, int, int)
+ * @see #filterAppAccess(AndroidPackage, int, int)
*/
public abstract boolean filterAppAccess(
@NonNull String packageName, int callingUid, int userId);
@@ -763,21 +761,29 @@
throws IOException;
/** Returns {@code true} if the specified component is enabled and matches the given flags. */
- public abstract boolean isEnabledAndMatches(
- @NonNull ComponentParseUtils.ParsedComponent component, int flags, int userId);
+ public abstract boolean isEnabledAndMatches(@NonNull ParsedMainComponent component, int flags,
+ int userId);
/** Returns {@code true} if the given user requires extra badging for icons. */
public abstract boolean userNeedsBadging(int userId);
/**
* Perform the given action for each package.
- * Note that packages lock will be held while performin the actions.
+ * Note that packages lock will be held while performing the actions.
*
* @param actionLocked action to be performed
*/
public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
/**
+ * Perform the given action for each {@link PackageSetting}.
+ * Note that packages lock will be held while performing the actions.
+ *
+ * @param actionLocked action to be performed
+ */
+ public abstract void forEachPackageSetting(Consumer<PackageSetting> actionLocked);
+
+ /**
* Perform the given action for each installed package for a user.
* Note that packages lock will be held while performin the actions.
*/
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index c8894e7..f1f5005 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -27,8 +27,10 @@
import android.database.ContentObserver;
import android.hardware.health.V1_0.HealthInfo;
import android.hardware.health.V2_0.IHealth;
-import android.hardware.health.V2_0.IHealthInfoCallback;
import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_1.BatteryCapacityLevel;
+import android.hardware.health.V2_1.Constants;
+import android.hardware.health.V2_1.IHealthInfoCallback;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.metrics.LogMaker;
@@ -145,6 +147,7 @@
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
+ private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -359,6 +362,9 @@
}
private boolean shouldShutdownLocked() {
+ if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+ return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+ }
if (mHealthInfo.batteryLevel > 0) {
return false;
}
@@ -416,22 +422,23 @@
}
}
- private void update(android.hardware.health.V2_0.HealthInfo info) {
+ private void update(android.hardware.health.V2_1.HealthInfo info) {
traceBegin("HealthInfoUpdate");
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
- info.legacy.batteryChargeCounter);
+ info.legacy.legacy.batteryChargeCounter);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
- info.legacy.batteryCurrent);
+ info.legacy.legacy.batteryCurrent);
synchronized (mLock) {
if (!mUpdatesStopped) {
- mHealthInfo = info.legacy;
+ mHealthInfo = info.legacy.legacy;
+ mHealthInfo2p1 = info;
// Process the new values.
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
- copy(mLastHealthInfo, info.legacy);
+ copy(mLastHealthInfo, info.legacy.legacy);
}
}
traceEnd();
@@ -485,7 +492,8 @@
mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
- mHealthInfo.batteryFullCharge);
+ mHealthInfo.batteryFullCharge,
+ mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
} catch (RemoteException e) {
// Should never happen.
}
@@ -1123,8 +1131,21 @@
private final class HealthHalCallback extends IHealthInfoCallback.Stub
implements HealthServiceWrapper.Callback {
@Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
+ android.hardware.health.V2_1.HealthInfo propsLatest =
+ new android.hardware.health.V2_1.HealthInfo();
+ propsLatest.legacy = props;
+
+ propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
+ propsLatest.batteryChargeTimeToFullNowSeconds =
+ Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+
+ BatteryService.this.update(propsLatest);
+ }
+
+ @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
BatteryService.this.update(props);
}
+
// on new service registered
@Override public void onRegistration(IHealth oldService, IHealth newService,
String instance) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f7eabac..e48ef5a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1534,7 +1534,8 @@
}
@Override
- public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+ public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+ int userId, String callingPackageName) {
// The basic principle is: if an app's traffic could possibly go over a
// network, without the app doing anything multinetwork-specific,
// (hence, by "default"), then include that network's capabilities in
@@ -1556,7 +1557,10 @@
NetworkAgentInfo nai = getDefaultNetwork();
NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
- result.put(nai.network, nc);
+ result.put(
+ nai.network,
+ maybeSanitizeLocationInfoForCaller(
+ nc, Binder.getCallingUid(), callingPackageName));
}
synchronized (mVpns) {
@@ -1566,10 +1570,12 @@
Network[] networks = vpn.getUnderlyingNetworks();
if (networks != null) {
for (Network network : networks) {
- nai = getNetworkAgentInfoForNetwork(network);
- nc = getNetworkCapabilitiesInternal(nai);
+ nc = getNetworkCapabilitiesInternal(network);
if (nc != null) {
- result.put(network, nc);
+ result.put(
+ network,
+ maybeSanitizeLocationInfoForCaller(
+ nc, Binder.getCallingUid(), callingPackageName));
}
}
}
@@ -1636,20 +1642,26 @@
}
}
+ private NetworkCapabilities getNetworkCapabilitiesInternal(Network network) {
+ return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+ }
+
private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
if (nai == null) return null;
synchronized (nai) {
if (nai.networkCapabilities == null) return null;
return networkCapabilitiesRestrictedForCallerPermissions(
- nai.networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
+ nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
}
}
@Override
- public NetworkCapabilities getNetworkCapabilities(Network network) {
+ public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
enforceAccessPermission();
- return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+ return maybeSanitizeLocationInfoForCaller(
+ getNetworkCapabilitiesInternal(network),
+ Binder.getCallingUid(), callingPackageName);
}
@VisibleForTesting
@@ -1665,20 +1677,34 @@
}
newNc.setAdministratorUids(Collections.EMPTY_LIST);
- maybeSanitizeLocationInfoForCaller(newNc, callerUid);
-
return newNc;
}
- private void maybeSanitizeLocationInfoForCaller(
- NetworkCapabilities nc, int callerUid) {
- // TODO(b/142072839): Conditionally reset the owner UID if the following
- // conditions are not met:
- // 1. The destination app is the network owner
- // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted
- // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted
- // 3. The user's location toggle is on
- nc.setOwnerUid(INVALID_UID);
+ @VisibleForTesting
+ @Nullable
+ NetworkCapabilities maybeSanitizeLocationInfoForCaller(
+ @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+ if (nc == null) {
+ return null;
+ }
+ final NetworkCapabilities newNc = new NetworkCapabilities(nc);
+ if (callerUid != newNc.getOwnerUid()) {
+ newNc.setOwnerUid(INVALID_UID);
+ return newNc;
+ }
+
+ Binder.withCleanCallingIdentity(
+ () -> {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+ // Caller does not have the requisite location permissions. Reset the
+ // owner's UID in the NetworkCapabilities.
+ newNc.setOwnerUid(INVALID_UID);
+ }
+ }
+ );
+
+ return newNc;
}
private LinkProperties linkPropertiesRestrictedForCallerPermissions(
@@ -1753,7 +1779,7 @@
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
- final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
+ final NetworkCapabilities caps = getNetworkCapabilitiesInternal(getActiveNetwork());
if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
@@ -5330,8 +5356,8 @@
}
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + request +
- (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+ return "uid/pid:" + mUid + "/" + mPid + " " + request
+ + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -6408,8 +6434,13 @@
}
switch (notificationType) {
case ConnectivityManager.CALLBACK_AVAILABLE: {
- putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
- networkAgent.networkCapabilities, nri.mPid, nri.mUid));
+ final NetworkCapabilities nc =
+ networkCapabilitiesRestrictedForCallerPermissions(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+ putParcelable(
+ bundle,
+ maybeSanitizeLocationInfoForCaller(
+ nc, nri.mUid, nri.request.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6422,9 +6453,13 @@
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
- final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions(
- networkAgent.networkCapabilities, nri.mPid, nri.mUid);
- putParcelable(bundle, nc);
+ final NetworkCapabilities netCap =
+ networkCapabilitiesRestrictedForCallerPermissions(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+ putParcelable(
+ bundle,
+ maybeSanitizeLocationInfoForCaller(
+ netCap, nri.mUid, nri.request.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 05820d2..7ec2a34 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.NonNull;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
@@ -44,29 +45,30 @@
/**
* {@hide}
*/
-public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
+public abstract class IntentResolver<F, R extends Object> {
final private static String TAG = "IntentResolver";
final private static boolean DEBUG = false;
final private static boolean localLOGV = DEBUG || false;
final private static boolean localVerificationLOGV = DEBUG || false;
public void addFilter(F f) {
+ IntentFilter intentFilter = getIntentFilter(f);
if (localLOGV) {
Slog.v(TAG, "Adding filter: " + f);
- f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
Slog.v(TAG, " Building Lookup Maps:");
}
mFilters.add(f);
- int numS = register_intent_filter(f, f.schemesIterator(),
+ int numS = register_intent_filter(f, intentFilter.schemesIterator(),
mSchemeToFilter, " Scheme: ");
int numT = register_mime_types(f, " Type: ");
if (numS == 0 && numT == 0) {
- register_intent_filter(f, f.actionsIterator(),
+ register_intent_filter(f, intentFilter.actionsIterator(),
mActionToFilter, " Action: ");
}
if (numT != 0) {
- register_intent_filter(f, f.actionsIterator(),
+ register_intent_filter(f, intentFilter.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
}
@@ -153,7 +155,7 @@
if (cur == null) {
break;
}
- if (filterEquals(cur, matching)) {
+ if (filterEquals(getIntentFilter(cur), matching)) {
if (res == null) {
res = new ArrayList<>();
}
@@ -178,7 +180,7 @@
} else {
ArrayList<F> res = null;
for (F cur : mFilters) {
- if (filterEquals(cur, matching)) {
+ if (filterEquals(getIntentFilter(cur), matching)) {
if (res == null) {
res = new ArrayList<>();
}
@@ -195,21 +197,22 @@
}
protected void removeFilterInternal(F f) {
+ IntentFilter intentFilter = getIntentFilter(f);
if (localLOGV) {
Slog.v(TAG, "Removing filter: " + f);
- f.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
+ intentFilter.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), " ");
Slog.v(TAG, " Cleaning Lookup Maps:");
}
- int numS = unregister_intent_filter(f, f.schemesIterator(),
+ int numS = unregister_intent_filter(f, intentFilter.schemesIterator(),
mSchemeToFilter, " Scheme: ");
int numT = unregister_mime_types(f, " Type: ");
if (numS == 0 && numT == 0) {
- unregister_intent_filter(f, f.actionsIterator(),
+ unregister_intent_filter(f, intentFilter.actionsIterator(),
mActionToFilter, " Action: ");
}
if (numT != 0) {
- unregister_intent_filter(f, f.actionsIterator(),
+ unregister_intent_filter(f, intentFilter.actionsIterator(),
mTypedActionToFilter, " TypedAction: ");
}
}
@@ -272,7 +275,7 @@
if (printer == null) {
printer = new PrintWriterPrinter(out);
}
- filter.dump(printer, fprefix + " ");
+ getIntentFilter(filter).dump(printer, fprefix + " ");
}
}
}
@@ -527,7 +530,7 @@
* @see android.content.IntentFilter#getAutoVerify()
*/
protected boolean isFilterVerified(F filter) {
- return filter.isVerified();
+ return getIntentFilter(filter).isVerified();
}
/**
@@ -591,7 +594,7 @@
}
private final int register_mime_types(F filter, String prefix) {
- final Iterator<String> i = filter.typesIterator();
+ final Iterator<String> i = getIntentFilter(filter).typesIterator();
if (i == null) {
return 0;
}
@@ -622,7 +625,7 @@
}
private final int unregister_mime_types(F filter, String prefix) {
- final Iterator<String> i = filter.typesIterator();
+ final Iterator<String> i = getIntentFilter(filter).typesIterator();
if (i == null) {
return 0;
}
@@ -762,12 +765,14 @@
}
// Are we verified ?
- if (filter.getAutoVerify()) {
+ IntentFilter intentFilter = getIntentFilter(filter);
+ if (intentFilter.getAutoVerify()) {
if (localVerificationLOGV || debug) {
Slog.v(TAG, " Filter verified: " + isFilterVerified(filter));
- int authorities = filter.countDataAuthorities();
+ int authorities = intentFilter.countDataAuthorities();
for (int z = 0; z < authorities; z++) {
- Slog.v(TAG, " " + filter.getDataAuthority(z).getHost());
+ Slog.v(TAG, " " + intentFilter.getDataAuthority(z)
+ .getHost());
}
}
}
@@ -780,12 +785,12 @@
continue;
}
- match = filter.match(action, resolvedType, scheme, data, categories, TAG);
+ match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG);
if (match >= 0) {
if (debug) Slog.v(TAG, " Filter matched! match=0x" +
Integer.toHexString(match) + " hasDefault="
- + filter.hasCategory(Intent.CATEGORY_DEFAULT));
- if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
+ + intentFilter.hasCategory(Intent.CATEGORY_DEFAULT));
+ if (!defaultOnly || intentFilter.hasCategory(Intent.CATEGORY_DEFAULT)) {
final R oneResult = newResult(filter, match, userId);
if (debug) Slog.v(TAG, " Created result: " + oneResult);
if (oneResult != null) {
@@ -793,7 +798,7 @@
if (debug) {
dumpFilter(logPrintWriter, " ", filter);
logPrintWriter.flush();
- filter.dump(logPrinter, " ");
+ intentFilter.dump(logPrinter, " ");
}
}
} else {
@@ -875,4 +880,11 @@
* All of the actions that have been registered and specified a MIME type.
*/
private final ArrayMap<String, F[]> mTypedActionToFilter = new ArrayMap<String, F[]>();
+
+ /**
+ * Rather than refactoring the entire class, this allows the input {@link F} to be a type
+ * other than {@link IntentFilter}, transforming it whenever necessary. It is valid to use
+ * {@link IntentFilter} directly as {@link F} and just return {@param input}.
+ */
+ protected abstract IntentFilter getIntentFilter(@NonNull F input);
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5db5115..d515332 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -862,10 +862,6 @@
}
}
- public void requestSetAllowed(boolean allowed) {
- mProvider.requestSetAllowed(allowed);
- }
-
public void onUserStarted(int userId) {
synchronized (mLock) {
// clear the user's enabled state in order to force a reevalution of whether the
@@ -2931,18 +2927,6 @@
private class LocalService extends LocationManagerInternal {
@Override
- public void requestSetProviderAllowed(String provider, boolean allowed) {
- Preconditions.checkArgument(provider != null, "invalid null provider");
-
- synchronized (mLock) {
- LocationProviderManager manager = getLocationProviderManager(provider);
- if (manager != null) {
- manager.requestSetAllowed(allowed);
- }
- }
- }
-
- @Override
public boolean isProviderEnabledForUser(@NonNull String provider, int userId) {
synchronized (mLock) {
LocationProviderManager manager = getLocationProviderManager(provider);
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 6fb7b26..93859b3 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -103,7 +103,7 @@
private static boolean PROP_PIN_CAMERA =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
"pin_camera",
- SystemProperties.getBoolean("pinner.pin_camera", true));
+ SystemProperties.getBoolean("pinner.pin_camera", false));
// Pin using pinlist.meta when pinning apps.
private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
"pinner.use_pinlist", true);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index a1ccd84..8900eee 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -100,7 +100,7 @@
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec
"com.android.bluetooth", // Bluetooth service
- "/system/bin/statsd", // Stats daemon
+ "/apex/com.android.os.statsd/bin/statsd", // Stats daemon
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 1099413..f16e3ce 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -29,6 +29,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.debug.AdbProtoEnums;
+import android.debug.AdbTransportType;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.Uri;
@@ -722,13 +723,21 @@
}
/**
- * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB hanler
- * thread. When {@code enabled} is {@code false}, this disallows ADB debugging and shuts
- * down the handler thread.
+ * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB handler
+ * thread. When {@code enabled} is {@code false}, this disallows ADB debugging for the given
+ * @{code transportType}. See {@link IAdbTransport} for all available transport types.
+ * If all transport types are disabled, the ADB handler thread will shut down.
*/
- public void setAdbEnabled(boolean enabled) {
- mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
- : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+ public void setAdbEnabled(boolean enabled, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
+ : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+ } else if (transportType == AdbTransportType.WIFI) {
+ // TODO(joshuaduong): Not implemented
+ } else {
+ throw new IllegalArgumentException(
+ "setAdbEnabled called with unimplemented transport type=" + transportType);
+ }
}
/**
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index c125b1b..0d161b9 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -15,19 +15,23 @@
*/
package com.android.server.adb;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
import android.debug.IAdbManager;
import android.debug.IAdbTransport;
+import android.debug.PairDevice;
import android.hardware.usb.UsbManager;
+import android.net.Uri;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -38,7 +42,6 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
@@ -50,6 +53,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.Map;
/**
* The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -77,7 +81,8 @@
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mAdbService.systemReady();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
- mAdbService.bootCompleted();
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::bootCompleted, mAdbService));
}
}
}
@@ -94,8 +99,14 @@
}
@Override
- public boolean isAdbEnabled() {
- return mAdbEnabled;
+ public boolean isAdbEnabled(byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ return mIsAdbUsbEnabled;
+ } else if (transportType == AdbTransportType.WIFI) {
+ return mIsAdbWifiEnabled;
+ }
+ throw new IllegalArgumentException(
+ "isAdbEnabled called with unimplemented transport type=" + transportType);
}
@Override
@@ -109,77 +120,70 @@
}
}
- private final class AdbHandler extends Handler {
- AdbHandler(Looper looper) {
- super(looper);
- try {
- /*
- * Use the normal bootmode persistent prop to maintain state of adb across
- * all boot modes.
- */
- mAdbEnabled = containsFunction(
- SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
- UsbManager.USB_FUNCTION_ADB);
+ private void initAdbState() {
+ try {
+ /*
+ * Use the normal bootmode persistent prop to maintain state of adb across
+ * all boot modes.
+ */
+ mIsAdbUsbEnabled = containsFunction(
+ SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
+ UsbManager.USB_FUNCTION_ADB);
+ // TODO(joshuaduong): Read the adb wifi state from a persistent system
+ // property (persist.sys.adb.wifi).
+ mIsAdbWifiEnabled = false;
- // register observer to listen for settings changes
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
- false, new AdbSettingsObserver());
- } catch (Exception e) {
- Slog.e(TAG, "Error initializing AdbHandler", e);
- }
- }
-
- private boolean containsFunction(String functions, String function) {
- int index = functions.indexOf(function);
- if (index < 0) return false;
- if (index > 0 && functions.charAt(index - 1) != ',') return false;
- int charAfter = index + function.length();
- if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
- return true;
- }
-
- public void sendMessage(int what, boolean arg) {
- removeMessages(what);
- Message m = Message.obtain(this, what);
- m.arg1 = (arg ? 1 : 0);
- sendMessage(m);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ENABLE_ADB:
- setAdbEnabled(msg.arg1 == 1);
- break;
- case MSG_BOOT_COMPLETED:
- if (mDebuggingManager != null) {
- mDebuggingManager.setAdbEnabled(mAdbEnabled);
- }
- break;
- }
+ // register observer to listen for settings changes
+ mObserver = new AdbSettingsObserver();
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+ false, mObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED),
+ false, mObserver);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in initAdbState", e);
}
}
+ private static boolean containsFunction(String functions, String function) {
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
+ }
+
private class AdbSettingsObserver extends ContentObserver {
+ private final Uri mAdbUsbUri = Settings.Global.getUriFor(Settings.Global.ADB_ENABLED);
+ private final Uri mAdbWifiUri = Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED);
+
AdbSettingsObserver() {
super(null);
}
@Override
- public void onChange(boolean selfChange) {
- boolean enable = (Settings.Global.getInt(mContentResolver,
- Settings.Global.ADB_ENABLED, 0) > 0);
- mHandler.sendMessage(MSG_ENABLE_ADB, enable);
+ public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
+ if (mAdbUsbUri.equals(uri)) {
+ boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
+ Settings.Global.ADB_ENABLED, 0) > 0);
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbEnabled, AdbService.this, shouldEnable,
+ AdbTransportType.USB));
+ } else if (mAdbWifiUri.equals(uri)) {
+ boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0) > 0);
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbEnabled, AdbService.this, shouldEnable,
+ AdbTransportType.WIFI));
+ }
}
}
private static final String TAG = "AdbService";
private static final boolean DEBUG = false;
- private static final int MSG_ENABLE_ADB = 1;
- private static final int MSG_BOOT_COMPLETED = 2;
-
/**
* The persistent property which stores whether adb is enabled or not.
* May also contain vendor-specific default functions for testing purposes.
@@ -188,12 +192,14 @@
private final Context mContext;
private final ContentResolver mContentResolver;
- private final AdbService.AdbHandler mHandler;
private final ArrayMap<IBinder, IAdbTransport> mTransports = new ArrayMap<>();
- private boolean mAdbEnabled;
+ private boolean mIsAdbUsbEnabled;
+ private boolean mIsAdbWifiEnabled;
private AdbDebuggingManager mDebuggingManager;
+ private ContentObserver mObserver;
+
private AdbService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
@@ -204,8 +210,7 @@
mDebuggingManager = new AdbDebuggingManager(context);
}
- mHandler = new AdbHandler(FgThread.get().getLooper());
-
+ initAdbState();
LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
}
@@ -219,7 +224,9 @@
// make sure the ADB_ENABLED setting value matches the current state
try {
Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+ Settings.Global.ADB_ENABLED, mIsAdbUsbEnabled ? 1 : 0);
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, mIsAdbWifiEnabled ? 1 : 0);
} catch (SecurityException e) {
// If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
Slog.d(TAG, "ADB_ENABLED is restricted.");
@@ -231,7 +238,10 @@
*/
public void bootCompleted() {
if (DEBUG) Slog.d(TAG, "boot completed");
- mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mIsAdbUsbEnabled, AdbTransportType.USB);
+ mDebuggingManager.setAdbEnabled(mIsAdbWifiEnabled, AdbTransportType.WIFI);
+ }
}
@Override
@@ -285,24 +295,82 @@
PackageManager.FEATURE_CAMERA_ANY);
}
- private void setAdbEnabled(boolean enable) {
- if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled);
+ @Override
+ public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ }
- if (enable == mAdbEnabled) {
+ @Override
+ public void denyWirelessDebugging() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ }
+
+ @Override
+ public Map<String, PairDevice> getPairedDevices() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ return null;
+ }
+
+ @Override
+ public void unpairDevice(String fingerprint) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ }
+
+ @Override
+ public void enablePairingByPairingCode() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ }
+
+ @Override
+ public void enablePairingByQrCode(String serviceName, String password) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ }
+
+ @Override
+ public void disablePairing() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ }
+
+ @Override
+ public int getAdbWirelessPort() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ // TODO(joshuaduong): NOT IMPLEMENTED
+ return 0;
+ }
+
+ private void setAdbEnabled(boolean enable, byte transportType) {
+ if (DEBUG) {
+ Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
+ + ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType="
+ + transportType);
+ }
+
+ if (transportType == AdbTransportType.USB && enable != mIsAdbUsbEnabled) {
+ mIsAdbUsbEnabled = enable;
+ } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
+ mIsAdbWifiEnabled = enable;
+ } else {
+ // No change
return;
}
- mAdbEnabled = enable;
for (IAdbTransport transport : mTransports.values()) {
try {
- transport.onAdbEnabled(enable);
+ transport.onAdbEnabled(enable, transportType);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to send onAdbEnabled to transport " + transport.toString());
}
}
if (mDebuggingManager != null) {
- mDebuggingManager.setAdbEnabled(enable);
+ mDebuggingManager.setAdbEnabled(enable, transportType);
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 50f43b5..2bcb28d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -62,7 +62,6 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -91,7 +90,6 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.webkit.WebViewZygote;
-import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.app.procstats.ServiceState;
@@ -4687,20 +4685,35 @@
}
// TODO: remove this toast after feature development is done
- private void showWhileInUsePermissionInFgsBlockedToastLocked(String callingPackage) {
- final Resources res = mAm.mContext.getResources();
- final String toastMsg = res.getString(
- R.string.allow_while_in_use_permission_in_fgs, callingPackage);
- mAm.mUiHandler.post(() -> {
- Toast.makeText(mAm.mContext, toastMsg, Toast.LENGTH_LONG).show();
- });
+ private void showWhileInUsePermissionInFgsBlockedNotificationLocked(String callingPackage,
+ String detailInfo) {
+ final Context context = mAm.mContext;
+ final String title = "Foreground Service While-in-use Permission Restricted";
+ final String content = "App affected:" + callingPackage + ", please file a bug report";
+ Notification.Builder n =
+ new Notification.Builder(context,
+ SystemNotificationChannels.ALERTS)
+ .setSmallIcon(R.drawable.stat_sys_vitals)
+ .setWhen(0)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setTicker(title)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setStyle(new Notification.BigTextStyle().bigText(detailInfo));
+ final NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
+ notificationManager.notifyAsUser(null,
+ SystemMessageProto.SystemMessage.NOTE_FOREGROUND_SERVICE_WHILE_IN_USE_PERMISSION,
+ n.build(), UserHandle.ALL);
}
// TODO: remove this toast after feature development is done
// show a toast message to ask user to file a bugreport so we know how many apps are impacted by
// the new background started foreground service while-in-use permission restriction.
- void showWhileInUseDebugToastLocked(int uid, int op, int mode) {
- StringBuilder sb = new StringBuilder();
+ void showWhileInUseDebugNotificationLocked(int uid, int op, int mode) {
+ StringBuilder packageNameBuilder = new StringBuilder();
+ StringBuilder detailInfoBuilder = new StringBuilder();
for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
if (pr.uid != uid) {
@@ -4713,17 +4726,22 @@
}
if (!r.mAllowWhileInUsePermissionInFgs
&& r.mInfoDenyWhileInUsePermissionInFgs != null) {
- Slog.wtf(TAG, r.mInfoDenyWhileInUsePermissionInFgs
- + " affected while-use-permission:" + AppOpsManager.opToPublicName(op));
- sb.append(r.mRecentCallingPackage + " ");
+ final String msg = r.mInfoDenyWhileInUsePermissionInFgs
+ + " affected while-in-use permission:"
+ + AppOpsManager.opToPublicName(op);
+ Slog.wtf(TAG, msg);
+ packageNameBuilder.append(r.mRecentCallingPackage + " ");
+ detailInfoBuilder.append(msg);
+ detailInfoBuilder.append("\n");
}
}
}
- final String callingPackageStr = sb.toString();
+ final String callingPackageStr = packageNameBuilder.toString();
if (mAm.mConstants.mFlagForegroundServiceStartsLoggingEnabled
&& !callingPackageStr.isEmpty()) {
- showWhileInUsePermissionInFgsBlockedToastLocked(callingPackageStr);
+ showWhileInUsePermissionInFgsBlockedNotificationLocked(callingPackageStr,
+ detailInfoBuilder.toString());
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9082807..a529f24 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1045,6 +1045,11 @@
}
@Override
+ protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {
+ return input;
+ }
+
+ @Override
protected BroadcastFilter[] newArray(int size) {
return new BroadcastFilter[size];
}
@@ -7266,7 +7271,7 @@
// Wait for the provider to be published...
final long timeout =
- SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
+ SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS;
boolean timedOut = false;
synchronized (cpr) {
while (cpr.provider == null) {
@@ -14605,6 +14610,11 @@
if (index < 0) {
ProcessList.remove(app.pid);
}
+
+ // Remove provider publish timeout because we will start a new timeout when the
+ // restarted process is attaching (if the process contains launching providers).
+ mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app);
+
mProcessList.addProcessNameLocked(app);
app.pendingStart = false;
mProcessList.startProcessLocked(app,
@@ -19333,7 +19343,8 @@
@Override
public void showWhileInUseDebugToast(int uid, int op, int mode) {
synchronized (ActivityManagerService.this) {
- ActivityManagerService.this.mServices.showWhileInUseDebugToastLocked(uid, op, mode);
+ ActivityManagerService.this.mServices.showWhileInUseDebugNotificationLocked(
+ uid, op, mode);
}
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ed6ace3..119394f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1135,7 +1135,7 @@
@Override
public void setBatteryState(final int status, final int health, final int plugType,
final int level, final int temp, final int volt, final int chargeUAh,
- final int chargeFullUAh) {
+ final int chargeFullUAh, final long chargeTimeToFullSeconds) {
enforceCallingPermission();
// BatteryService calls us here and we may update external state. It would be wrong
@@ -1147,7 +1147,7 @@
// The battery state has not changed, so we don't need to sync external
// stats immediately.
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
- chargeUAh, chargeFullUAh);
+ chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
return;
}
}
@@ -1160,7 +1160,7 @@
mWorker.scheduleRunnable(() -> {
synchronized (mStats) {
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
- chargeUAh, chargeFullUAh);
+ chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
}
});
});
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index a03f0bb..48ceba9 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.annotation.NonNull;
import android.app.ActivityThread;
import android.content.Context;
import android.database.ContentObserver;
@@ -32,6 +33,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Helper class for watching a set of core settings which the framework
@@ -42,16 +44,20 @@
final class CoreSettingsObserver extends ContentObserver {
private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
- private static class DeviceConfigEntry {
+ private static class DeviceConfigEntry<T> {
String namespace;
String flag;
String coreSettingKey;
- Class<?> type;
- DeviceConfigEntry(String namespace, String flag, String coreSettingKey, Class<?> type) {
+ Class<T> type;
+ T defaultValue;
+
+ DeviceConfigEntry(String namespace, String flag, String coreSettingKey, Class<T> type,
+ @NonNull T defaultValue) {
this.namespace = namespace;
this.flag = flag;
this.coreSettingKey = coreSettingKey;
this.type = type;
+ this.defaultValue = Objects.requireNonNull(defaultValue);
}
}
@@ -105,24 +111,34 @@
sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class);
// add other global settings here...
- sDeviceConfigEntries.add(new DeviceConfigEntry(
- DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_CONTROL,
- WidgetFlags.KEY_ENABLE_CURSOR_CONTROL, boolean.class));
- sDeviceConfigEntries.add(new DeviceConfigEntry(
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
+ WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
+ WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
+ DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES,
+ WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES, boolean.class,
+ WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT,
- WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, int.class));
- sDeviceConfigEntries.add(new DeviceConfigEntry(
+ WidgetFlags.KEY_INSERTION_HANDLE_DELTA_HEIGHT, int.class,
+ WidgetFlags.INSERTION_HANDLE_DELTA_HEIGHT_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.INSERTION_HANDLE_OPACITY,
- WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, int.class));
- sDeviceConfigEntries.add(new DeviceConfigEntry(
+ WidgetFlags.KEY_INSERTION_HANDLE_OPACITY, int.class,
+ WidgetFlags.INSERTION_HANDLE_OPACITY_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.ENABLE_NEW_MAGNIFIER,
- WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, boolean.class));
- sDeviceConfigEntries.add(new DeviceConfigEntry(
+ WidgetFlags.KEY_ENABLE_NEW_MAGNIFIER, boolean.class,
+ WidgetFlags.ENABLE_NEW_MAGNIFIER_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Float>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ZOOM_FACTOR,
- WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, float.class));
- sDeviceConfigEntries.add(new DeviceConfigEntry(
+ WidgetFlags.KEY_MAGNIFIER_ZOOM_FACTOR, float.class,
+ WidgetFlags.MAGNIFIER_ZOOM_FACTOR_DEFAULT));
+ sDeviceConfigEntries.add(new DeviceConfigEntry<Float>(
DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.MAGNIFIER_ASPECT_RATIO,
- WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class));
+ WidgetFlags.KEY_MAGNIFIER_ASPECT_RATIO, float.class,
+ WidgetFlags.MAGNIFIER_ASPECT_RATIO_DEFAULT));
// add other device configs here...
}
@@ -216,23 +232,29 @@
}
}
+ @SuppressWarnings("unchecked")
private void populateSettingsFromDeviceConfig() {
- for (DeviceConfigEntry entry : sDeviceConfigEntries) {
+ for (DeviceConfigEntry<?> entry : sDeviceConfigEntries) {
if (entry.type == String.class) {
+ String defaultValue = ((DeviceConfigEntry<String>) entry).defaultValue;
mCoreSettings.putString(entry.coreSettingKey,
- DeviceConfig.getString(entry.namespace, entry.flag, ""));
+ DeviceConfig.getString(entry.namespace, entry.flag, defaultValue));
} else if (entry.type == int.class) {
+ int defaultValue = ((DeviceConfigEntry<Integer>) entry).defaultValue;
mCoreSettings.putInt(entry.coreSettingKey,
- DeviceConfig.getInt(entry.namespace, entry.flag, 0));
+ DeviceConfig.getInt(entry.namespace, entry.flag, defaultValue));
} else if (entry.type == float.class) {
+ float defaultValue = ((DeviceConfigEntry<Float>) entry).defaultValue;
mCoreSettings.putFloat(entry.coreSettingKey,
- DeviceConfig.getFloat(entry.namespace, entry.flag, 0));
+ DeviceConfig.getFloat(entry.namespace, entry.flag, defaultValue));
} else if (entry.type == long.class) {
+ long defaultValue = ((DeviceConfigEntry<Long>) entry).defaultValue;
mCoreSettings.putLong(entry.coreSettingKey,
- DeviceConfig.getLong(entry.namespace, entry.flag, 0));
+ DeviceConfig.getLong(entry.namespace, entry.flag, defaultValue));
} else if (entry.type == boolean.class) {
+ boolean defaultValue = ((DeviceConfigEntry<Boolean>) entry).defaultValue;
mCoreSettings.putInt(entry.coreSettingKey,
- DeviceConfig.getBoolean(entry.namespace, entry.flag, false) ? 1 : 0);
+ DeviceConfig.getBoolean(entry.namespace, entry.flag, defaultValue) ? 1 : 0);
}
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index a2ae678..c239feb1 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1128,6 +1128,7 @@
app.setCurRawAdj(app.maxAdj);
app.setHasForegroundActivities(false);
app.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
+ app.curCapability = PROCESS_CAPABILITY_ALL;
app.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT);
// System processes can do UI, and when they do we want to have
// them trim their memory after the user leaves the UI. To
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0dc44f7..22559c4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -345,6 +345,14 @@
@EnabledAfter(targetSdkVersion = VersionCodes.Q)
private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+ /**
+ * Apps have no access to the private data directories of any other app, even if the other
+ * app has made them world-readable.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+ private static final long APP_DATA_DIRECTORY_ISOLATION = 143937733; // See b/143937733
+
ActivityManagerService mService = null;
// To kill process groups asynchronously
@@ -2070,7 +2078,14 @@
}
final int minTargetSdk = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ANDROID_APP_DATA_ISOLATION_MIN_SDK, Build.VERSION_CODES.R);
- return app.info.targetSdkVersion >= minTargetSdk;
+ if (app.info.targetSdkVersion < minTargetSdk) {
+ return false;
+ }
+
+ // TODO(b/147266020): Remove non-standard gating above & switch to isChangeEnabled.
+ mPlatformCompat.reportChange(APP_DATA_DIRECTORY_ISOLATION, app.info);
+
+ return true;
}
private Map<String, Pair<String, Long>> getPackageAppDataInfoMap(PackageManagerInternal pmInt,
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 5d8a0f6..4670d58 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -401,10 +401,10 @@
pw.print(prefix); pw.print("hasStartedWhitelistingBgActivityStarts=");
pw.println(mHasStartedWhitelistingBgActivityStarts);
}
- if (mAllowWhileInUsePermissionInFgs) {
- pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
- pw.println(mAllowWhileInUsePermissionInFgs);
- }
+ pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
+ pw.println(mAllowWhileInUsePermissionInFgs);
+ pw.print(prefix); pw.print("recentCallingPackage=");
+ pw.println(mRecentCallingPackage);
if (delayed) {
pw.print(prefix); pw.print("delayed="); pw.println(delayed);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 40acfe1..7462f7f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -98,8 +98,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
+import android.content.pm.parsing.component.ParsedFeature;
import android.database.ContentObserver;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
@@ -156,6 +155,7 @@
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.pm.PackageList;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.util.EmptyArray;
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 204f072..8ed221d 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -311,6 +311,7 @@
}
authenticator = new FingerprintAuthenticator(fingerprintService);
+ fingerprintService.initConfiguredStrength(config.mStrength);
break;
case TYPE_FACE:
@@ -322,6 +323,7 @@
}
authenticator = new FaceAuthenticator(faceService);
+ faceService.initConfiguredStrength(config.mStrength);
break;
case TYPE_IRIS:
@@ -333,6 +335,7 @@
}
authenticator = new IrisAuthenticator(irisService);
+ irisService.initConfiguredStrength(config.mStrength);
break;
default:
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 0e70994..74c70df 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -31,6 +31,7 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricNativeHandle;
import android.hardware.biometrics.IBiometricService;
@@ -106,6 +107,7 @@
private PerformanceStats mPerformanceStats;
protected int mCurrentUserId = UserHandle.USER_NULL;
protected long mHalDeviceId;
+ private int mOEMStrength; // Tracks the OEM configured biometric modality strength
// Tracks if the current authentication makes use of CryptoObjects.
protected boolean mIsCrypto;
// Normal authentications are tracked by mPerformanceMap.
@@ -681,6 +683,20 @@
statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH);
}
+ protected void initConfiguredStrengthInternal(int strength) {
+ if (DEBUG) {
+ Slog.d(getTag(), "initConfiguredStrengthInternal(" + strength + ")");
+ }
+ mOEMStrength = strength;
+ }
+
+ protected boolean isStrongBiometric() {
+ // TODO(b/141025588): need to calculate actual strength when downgrading tiers
+ final int biometricBits = mOEMStrength
+ & BiometricManager.Authenticators.BIOMETRIC_MIN_STRENGTH;
+ return biometricBits == BiometricManager.Authenticators.BIOMETRIC_STRONG;
+ }
+
protected ClientMonitor getCurrentClient() {
return mCurrentClient;
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 31c3d4d..a87a455 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -740,6 +740,12 @@
}
return 0;
}
+
+ @Override // Binder call
+ public void initConfiguredStrength(int strength) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ initConfiguredStrengthInternal(strength);
+ }
}
/**
@@ -809,7 +815,7 @@
if (mFaceServiceReceiver != null) {
if (biometric == null || biometric instanceof Face) {
mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face) biometric,
- userId);
+ userId, isStrongBiometric());
} else {
Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 0a61988..83aa9cf 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
@@ -462,6 +463,12 @@
checkPermission(MANAGE_FINGERPRINT);
mClientActiveCallbacks.remove(callback);
}
+
+ @Override // Binder call
+ public void initConfiguredStrength(int strength) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ initConfiguredStrengthInternal(strength);
+ }
}
/**
@@ -526,8 +533,8 @@
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
if (biometric == null || biometric instanceof Fingerprint) {
- mFingerprintServiceReceiver
- .onAuthenticationSucceeded(deviceId, (Fingerprint) biometric, userId);
+ mFingerprintServiceReceiver.onAuthenticationSucceeded(deviceId,
+ (Fingerprint) biometric, userId, isStrongBiometric());
} else {
Slog.e(TAG, "onAuthenticationSucceeded received non-fingerprint biometric");
}
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index 2817315..903ae6b 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -16,9 +16,12 @@
package com.android.server.biometrics.iris;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.iris.IIrisService;
import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
@@ -42,6 +45,17 @@
private static final String TAG = "IrisService";
/**
+ * Receives the incoming binder calls from IrisManager.
+ */
+ private final class IrisServiceWrapper extends IIrisService.Stub {
+ @Override // Binder call
+ public void initConfiguredStrength(int strength) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ initConfiguredStrengthInternal(strength);
+ }
+ }
+
+ /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -57,6 +71,7 @@
@Override
public void onStart() {
super.onStart();
+ publishBinderService(Context.IRIS_SERVICE, new IrisServiceWrapper());
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3138639..e484ca0 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -690,13 +690,14 @@
// Prefer VPN profiles, if any exist.
VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage, keyStore);
if (profile != null) {
- startVpnProfilePrivileged(profile, alwaysOnPackage);
+ startVpnProfilePrivileged(profile, alwaysOnPackage,
+ null /* keyStore for private key retrieval - unneeded */);
// If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
// correctly parsed, and the VPN has started running in a different thread. The only
// other possibility is that the above call threw an exception, which will be
// caught below, and returns false (clearing the always-on VPN). Once started, the
- // Platform VPN cannot permanantly fail, and is resiliant to temporary failures. It
+ // Platform VPN cannot permanently fail, and is resilient to temporary failures. It
// will continue retrying until shut down by the user, or always-on is toggled off.
return true;
}
@@ -818,6 +819,7 @@
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
+ @GuardedBy("this")
private void prepareInternal(String newPackage) {
long token = Binder.clearCallingIdentity();
try {
@@ -1943,6 +1945,27 @@
// Prepare arguments for racoon.
String[] racoon = null;
switch (profile.type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ // Secret key is still just the alias (not the actual private key). The private key
+ // is retrieved from the KeyStore during conversion of the VpnProfile to an
+ // Ikev2VpnProfile.
+ profile.ipsecSecret = Ikev2VpnProfile.PREFIX_KEYSTORE_ALIAS + privateKey;
+ profile.ipsecUserCert = userCert;
+ // Fallthrough
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ profile.ipsecCaCert = caCert;
+
+ // Start VPN profile
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+ return;
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ // Ikev2VpnProfiles expect a base64-encoded preshared key.
+ profile.ipsecSecret =
+ Ikev2VpnProfile.encodeForIpsecSecret(profile.ipsecSecret.getBytes());
+
+ // Start VPN profile
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+ return;
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
racoon = new String[] {
iface, profile.server, "udppsk", profile.ipsecIdentifier,
@@ -2949,24 +2972,35 @@
throw new IllegalArgumentException("No profile found for " + packageName);
}
- startVpnProfilePrivileged(profile, packageName);
+ startVpnProfilePrivileged(profile, packageName,
+ null /* keyStore for private key retrieval - unneeded */);
});
}
- private void startVpnProfilePrivileged(
- @NonNull VpnProfile profile, @NonNull String packageName) {
- // Ensure that no other previous instance is running.
- if (mVpnRunner != null) {
- mVpnRunner.exit();
- mVpnRunner = null;
- }
+ private synchronized void startVpnProfilePrivileged(
+ @NonNull VpnProfile profile, @NonNull String packageName, @Nullable KeyStore keyStore) {
+ // Make sure VPN is prepared. This method can be called by user apps via startVpnProfile(),
+ // by the Setting app via startLegacyVpn(), or by ConnectivityService via
+ // startAlwaysOnVpn(), so this is the common place to prepare the VPN. This also has the
+ // nice property of ensuring there are no other VpnRunner instances running.
+ prepareInternal(packageName);
updateState(DetailedState.CONNECTING, "startPlatformVpn");
try {
// Build basic config
mConfig = new VpnConfig();
- mConfig.user = packageName;
- mConfig.isMetered = profile.isMetered;
+ if (VpnConfig.LEGACY_VPN.equals(packageName)) {
+ mConfig.legacy = true;
+ mConfig.session = profile.name;
+ mConfig.user = profile.key;
+
+ // TODO: Add support for configuring meteredness via Settings. Until then, use a
+ // safe default.
+ mConfig.isMetered = true;
+ } else {
+ mConfig.user = packageName;
+ mConfig.isMetered = profile.isMetered;
+ }
mConfig.startTime = SystemClock.elapsedRealtime();
mConfig.proxyInfo = profile.proxy;
@@ -2974,7 +3008,8 @@
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
- mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
+ mVpnRunner =
+ new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile, keyStore));
mVpnRunner.start();
break;
default:
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 8bbeabf..7cb8458 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -73,7 +73,7 @@
private static final int GLOBAL_ID = -1;
// The tolerance within which we consider something approximately equals.
- private static final float EPSILON = 0.01f;
+ private static final float FLOAT_TOLERANCE = 0.01f;
private final Object mLock = new Object();
private final Context mContext;
@@ -267,8 +267,8 @@
// Some refresh rates are calculated based on frame timings, so they aren't *exactly*
// equal to expected refresh rate. Given that, we apply a bit of tolerance to this
// comparison.
- if (refreshRate < (minRefreshRate - EPSILON)
- || refreshRate > (maxRefreshRate + EPSILON)) {
+ if (refreshRate < (minRefreshRate - FLOAT_TOLERANCE)
+ || refreshRate > (maxRefreshRate + FLOAT_TOLERANCE)) {
if (DEBUG) {
Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ ", outside refresh rate bounds"
@@ -487,12 +487,18 @@
public RefreshRateRange() {}
public RefreshRateRange(float min, float max) {
- if (min < 0 || max < 0 || min > max) {
+ if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
+ min + " " + max);
this.min = this.max = 0;
return;
}
+ if (min > max) {
+ // Min and max are within epsilon of each other, but in the wrong order.
+ float t = min;
+ min = max;
+ max = t;
+ }
this.min = min;
this.max = max;
}
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index c2af29c..1139d28 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -16,6 +16,7 @@
package com.android.server.firewall;
+import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Intent;
@@ -521,6 +522,11 @@
return;
}
+ @Override
+ protected IntentFilter getIntentFilter(@NonNull FirewallIntentFilter input) {
+ return input;
+ }
+
public void queryByComponent(ComponentName componentName, List<Rule> candidateRules) {
Rule[] rules = mRulesByComponent.get(componentName);
if (rules != null) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 9f76e1e..eac2d24 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,12 +18,11 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.content.ComponentName;
-import android.view.autofill.AutofillId;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputMethodInfo;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.server.LocalServices;
import java.util.Collections;
@@ -74,13 +73,11 @@
* Called by the Autofill Frameworks to request an {@link InlineSuggestionsRequest} from
* the input method.
*
- * @param componentName {@link ComponentName} of current app/activity.
- * @param autofillId {@link AutofillId} of currently focused field.
+ * @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
* @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
*/
public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- ComponentName componentName, AutofillId autofillId,
- IInlineSuggestionsRequestCallback cb);
+ InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb);
/**
* Force switch to the enabled input method by {@code imeId} for current user. If the input
@@ -124,7 +121,7 @@
@Override
public void onCreateInlineSuggestionsRequest(int userId,
- ComponentName componentName, AutofillId autofillId,
+ InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback cb) {
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 47622f3..87262a8 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -109,7 +109,6 @@
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.autofill.AutofillId;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InputBinding;
@@ -150,6 +149,7 @@
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.internal.view.InputBindResult;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
@@ -1388,6 +1388,44 @@
}
}
+ private static final class UserSwitchHandlerTask implements Runnable {
+ final InputMethodManagerService mService;
+
+ @UserIdInt
+ final int mToUserId;
+
+ @Nullable
+ IInputMethodClient mClientToBeReset;
+
+ UserSwitchHandlerTask(InputMethodManagerService service, @UserIdInt int toUserId,
+ @Nullable IInputMethodClient clientToBeReset) {
+ mService = service;
+ mToUserId = toUserId;
+ mClientToBeReset = clientToBeReset;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mService.mMethodMap) {
+ if (mService.mUserSwitchHandlerTask != this) {
+ // This task was already canceled before it is handled here. So do nothing.
+ return;
+ }
+ mService.switchUserOnHandlerLocked(mService.mUserSwitchHandlerTask.mToUserId,
+ mClientToBeReset);
+ mService.mUserSwitchHandlerTask = null;
+ }
+ }
+ }
+
+ /**
+ * When non-{@code null}, this represents pending user-switch task, which is to be executed as
+ * a handler callback. This needs to be set and unset only within the lock.
+ */
+ @Nullable
+ @GuardedBy("mMethodMap")
+ private UserSwitchHandlerTask mUserSwitchHandlerTask;
+
public static final class Lifecycle extends SystemService {
private InputMethodManagerService mService;
@@ -1406,8 +1444,9 @@
@Override
public void onSwitchUser(@UserIdInt int userHandle) {
// Called on ActivityManager thread.
- // TODO: Dispatch this to a worker thread as needed.
- mService.onSwitchUser(userHandle);
+ synchronized (mService.mMethodMap) {
+ mService.scheduleSwitchUserTaskLocked(userHandle, null /* clientToBeReset */);
+ }
}
@Override
@@ -1447,10 +1486,20 @@
}
}
- void onSwitchUser(@UserIdInt int userId) {
- synchronized (mMethodMap) {
- switchUserLocked(userId);
+ @GuardedBy("mMethodMap")
+ void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
+ @Nullable IInputMethodClient clientToBeReset) {
+ if (mUserSwitchHandlerTask != null) {
+ if (mUserSwitchHandlerTask.mToUserId == userId) {
+ mUserSwitchHandlerTask.mClientToBeReset = clientToBeReset;
+ return;
+ }
+ mHandler.removeCallbacks(mUserSwitchHandlerTask);
}
+ final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
+ clientToBeReset);
+ mUserSwitchHandlerTask = task;
+ mHandler.post(task);
}
public InputMethodManagerService(Context context) {
@@ -1538,7 +1587,8 @@
}
@GuardedBy("mMethodMap")
- private void switchUserLocked(int newUserId) {
+ private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
+ IInputMethodClient clientToBeReset) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
@@ -1589,6 +1639,18 @@
+ " selectedIme=" + mSettings.getSelectedInputMethod());
mLastSwitchUserId = newUserId;
+
+ if (mIsInteractive && clientToBeReset != null) {
+ final ClientState cs = mClients.get(clientToBeReset.asBinder());
+ if (cs == null) {
+ // The client is already gone.
+ return;
+ }
+ try {
+ cs.client.scheduleStartInputIfNecessary(mInFullscreenMode);
+ } catch (RemoteException e) {
+ }
+ }
}
void updateCurrentProfileIds() {
@@ -1812,16 +1874,14 @@
@GuardedBy("mMethodMap")
private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
- ComponentName componentName, AutofillId autofillId,
- IInlineSuggestionsRequestCallback callback) {
+ InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
try {
if (userId == mSettings.getCurrentUserId() && imi != null
&& imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
executeOrSendMessage(mCurMethod,
- mCaller.obtainMessageOOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
- componentName, autofillId,
- new InlineSuggestionsRequestCallbackDecorator(callback,
+ mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
+ requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback,
imi.getPackageName())));
} else {
callback.onInlineSuggestionsUnsupported();
@@ -3074,6 +3134,22 @@
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
+ if (mUserSwitchHandlerTask != null) {
+ // There is already an on-going pending user switch task.
+ final int nextUserId = mUserSwitchHandlerTask.mToUserId;
+ if (userId == nextUserId) {
+ scheduleSwitchUserTaskLocked(userId, cs.client);
+ return InputBindResult.USER_SWITCHING;
+ }
+ for (int profileId : mUserManager.getProfileIdsWithDisabled(nextUserId)) {
+ if (profileId == userId) {
+ scheduleSwitchUserTaskLocked(userId, cs.client);
+ return InputBindResult.USER_SWITCHING;
+ }
+ }
+ return InputBindResult.INVALID_USER;
+ }
+
// cross-profile access is always allowed here to allow profile-switching.
if (!mSettings.isCurrentProfile(userId)) {
Slog.w(TAG, "A background user is requesting window. Hiding IME.");
@@ -3085,8 +3161,10 @@
}
if (userId != mSettings.getCurrentUserId()) {
- switchUserLocked(userId);
+ scheduleSwitchUserTaskLocked(userId, cs.client);
+ return InputBindResult.USER_SWITCHING;
}
+
// Master feature flag that overrides other conditions and forces IME preRendering.
if (DEBUG) {
Slog.v(TAG, "IME PreRendering MASTER flag: "
@@ -3972,13 +4050,13 @@
// ---------------------------------------------------------------
case MSG_INLINE_SUGGESTIONS_REQUEST:
args = (SomeArgs) msg.obj;
- final ComponentName componentName = (ComponentName) args.arg2;
- final AutofillId autofillId = (AutofillId) args.arg3;
+ final InlineSuggestionsRequestInfo requestInfo =
+ (InlineSuggestionsRequestInfo) args.arg2;
final IInlineSuggestionsRequestCallback callback =
- (IInlineSuggestionsRequestCallback) args.arg4;
+ (IInlineSuggestionsRequestCallback) args.arg3;
try {
- ((IInputMethod) args.arg1).onCreateInlineSuggestionsRequest(componentName,
- autofillId, callback);
+ ((IInputMethod) args.arg1).onCreateInlineSuggestionsRequest(requestInfo,
+ callback);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
}
@@ -4549,10 +4627,10 @@
}
private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- ComponentName componentName, AutofillId autofillId,
+ InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback callback) {
synchronized (mMethodMap) {
- onCreateInlineSuggestionsRequestLocked(userId, componentName, autofillId, callback);
+ onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
}
}
@@ -4620,9 +4698,9 @@
}
@Override
- public void onCreateInlineSuggestionsRequest(int userId, ComponentName componentName,
- AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
- mService.onCreateInlineSuggestionsRequest(userId, componentName, autofillId, cb);
+ public void onCreateInlineSuggestionsRequest(int userId,
+ InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
+ mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb);
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 54af694..4904061 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -63,7 +63,6 @@
import android.util.SparseArray;
import android.view.InputChannel;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
-import android.view.autofill.AutofillId;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputMethodInfo;
@@ -88,6 +87,7 @@
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.InlineSuggestionsRequestInfo;
import com.android.internal.view.InputBindResult;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -192,7 +192,7 @@
@Override
public void onCreateInlineSuggestionsRequest(int userId,
- ComponentName componentName, AutofillId autofillId,
+ InlineSuggestionsRequestInfo requestInfo,
IInlineSuggestionsRequestCallback cb) {
try {
//TODO(b/137800469): support multi client IMEs.
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index b9a30bb..63054cf 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -45,9 +45,7 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.Signature;
import android.content.pm.SigningInfo;
-import android.content.pm.parsing.ApkParseUtils;
-import android.content.pm.parsing.PackageInfoUtils;
-import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -64,6 +62,9 @@
import com.android.server.integrity.engine.RuleEvaluationEngine;
import com.android.server.integrity.model.IntegrityCheckResult;
import com.android.server.integrity.model.RuleMetadata;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -508,13 +509,13 @@
throw new IllegalArgumentException("Installation path is null, package not found");
}
- PackageParser parser = new PackageParser();
+ PackageParser2 parser = new PackageParser2(null, false, null, null, null);
try {
- ParsedPackage pkg = parser.parseParsedPackage(installationPath, 0, false);
+ ParsedPackage pkg = parser.parsePackage(installationPath, 0, false);
int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA;
- ApkParseUtils.collectCertificates(pkg, false);
+ pkg.setSigningDetails(ParsingPackageUtils.collectCertificates(pkg, false));
return PackageInfoUtils.generate(pkg, null, flags, 0, 0, null, new PackageUserState(),
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId(), null);
} catch (Exception e) {
throw new IllegalArgumentException("Exception reading " + dataUri, e);
}
diff --git a/services/core/java/com/android/server/integrity/engine/RuleLoader.java b/services/core/java/com/android/server/integrity/engine/RuleLoader.java
deleted file mode 100644
index 4ba2bfb..0000000
--- a/services/core/java/com/android/server/integrity/engine/RuleLoader.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.server.integrity.engine;
-
-import android.content.integrity.Rule;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A helper class for loading rules to the rule evaluation engine.
- *
- * <p>Expose fine-grained APIs for loading rules to be passed to the rule evaluation engine.
- *
- * <p>It supports:
- * <ul>
- * <li>Loading rules based on some keys, such as PACKAGE_NAME and APP_CERT.</li>
- * </ul>
- *
- * <p>It does NOT support:
- * <ul>
- * <li>Loading the list of all rules.</li>
- * <li>Merging rules resulting from different APIs.</li>
- * </ul>
- */
-final class RuleLoader {
-
- List<Rule> loadRulesByPackageName(String packageName) {
- // TODO: Add logic based on rule storage.
- return new ArrayList<>();
- }
-
- List<Rule> loadRulesByAppCertificate(String appCertificate) {
- // TODO: Add logic based on rule storage.
- return new ArrayList<>();
- }
-
- List<Rule> loadRulesByInstallerName(String installerName) {
- // TODO: Add logic based on rule storage.
- return new ArrayList<>();
- }
-
- List<Rule> loadRulesByInstallerCertificate(String installerCertificate) {
- // TODO: Add logic based on rule storage.
- return new ArrayList<>();
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 11e8d91..a290eb3 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -68,8 +68,7 @@
}
private List<Rule> parseRules(
- RandomAccessInputStream randomAccessInputStream,
- List<RuleIndexRange> indexRanges)
+ RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
throws IOException {
// Read the rule binary file format version.
@@ -96,8 +95,7 @@
}
private List<Rule> parseIndexedRules(
- RandomAccessInputStream randomAccessInputStream,
- List<RuleIndexRange> indexRanges)
+ RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
throws IOException {
List<Rule> parsedRules = new ArrayList<>();
@@ -172,6 +170,7 @@
case AtomicFormula.APP_CERTIFICATE:
case AtomicFormula.INSTALLER_NAME:
case AtomicFormula.INSTALLER_CERTIFICATE:
+ case AtomicFormula.STAMP_CERTIFICATE_HASH:
boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue);
@@ -183,6 +182,7 @@
long longValue = (upper << 32) | lower;
return new AtomicFormula.LongAtomicFormula(key, operator, longValue);
case AtomicFormula.PRE_INSTALLED:
+ case AtomicFormula.STAMP_TRUSTED:
boolean booleanValue = getBooleanValue(bitInputStream);
return new AtomicFormula.BooleanAtomicFormula(key, booleanValue);
default:
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 433ec43..e9d94a5 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -366,21 +366,6 @@
protected abstract void onExtraCommand(int uid, int pid, String command, Bundle extras);
/**
- * Requests a provider to enable itself for the given user id.
- */
- public final void requestSetAllowed(boolean allowed) {
- // all calls into the provider must be moved onto the provider thread to prevent deadlock
- mExecutor.execute(
- obtainRunnable(AbstractLocationProvider::onRequestSetAllowed, this, allowed)
- .recycleOnUse());
- }
-
- /**
- * Always invoked on the provider executor.
- */
- protected abstract void onRequestSetAllowed(boolean allowed);
-
- /**
* Dumps debug or log information. May be invoked from any thread.
*/
public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5f44e04..685fb9e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1229,11 +1229,6 @@
}
}
- @Override
- protected void onRequestSetAllowed(boolean allowed) {
- // do nothing - the gnss provider is always allowed
- }
-
private void deleteAidingData(Bundle extras) {
int flags;
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 96ffaa6..87208a7 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -205,14 +205,6 @@
}
@Override
- public void onRequestSetAllowed(boolean allowed) {
- mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- service.requestSetAllowed(allowed);
- });
- }
-
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mServiceWatcher.dump(fd, pw, args);
}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index b45b660..5ec06ca 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -64,11 +64,6 @@
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
@Override
- protected void onRequestSetAllowed(boolean allowed) {
- setAllowed(allowed);
- }
-
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("last mock location=" + mLocation);
}
diff --git a/services/core/java/com/android/server/location/MockableLocationProvider.java b/services/core/java/com/android/server/location/MockableLocationProvider.java
index f43669e..0f358e9 100644
--- a/services/core/java/com/android/server/location/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/MockableLocationProvider.java
@@ -224,15 +224,6 @@
}
}
- @Override
- protected void onRequestSetAllowed(boolean allowed) {
- synchronized (mOwnerLock) {
- if (mProvider != null) {
- mProvider.onRequestSetAllowed(allowed);
- }
- }
- }
-
/**
* Dumps the current provider implementation.
*/
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 54dffff..1ba38cc 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -79,10 +79,5 @@
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
@Override
- protected void onRequestSetAllowed(boolean allowed) {
- // do nothing - the passive provider is always allowed
- }
-
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 1f4048f..15dfab9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.READ_CONTACTS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -178,6 +179,7 @@
public class LockSettingsService extends ILockSettings.Stub {
private static final String TAG = "LockSettingsService";
private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
+ private static final String BIOMETRIC_PERMISSION = MANAGE_BIOMETRIC;
private static final boolean DEBUG = false;
private static final int PROFILE_KEY_IV_SIZE = 12;
@@ -1050,6 +1052,10 @@
}
}
+ private final void checkBiometricPermission() {
+ mContext.enforceCallingOrSelfPermission(BIOMETRIC_PERMISSION, "LockSettingsBiometric");
+ }
+
@Override
public boolean hasSecureLockScreen() {
return mHasSecureLockScreen;
@@ -2304,6 +2310,18 @@
}
@Override
+ public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ checkBiometricPermission();
+ mStrongAuth.reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+ }
+
+ @Override
+ public void scheduleNonStrongBiometricIdleTimeout(int userId) {
+ checkBiometricPermission();
+ mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId);
+ }
+
+ @Override
public void userPresent(int userId) {
checkWritePermission(userId);
mStrongAuth.reportUnlock(userId);
@@ -3191,6 +3209,12 @@
mStorage.dump(pw);
pw.println();
pw.decreaseIndent();
+
+ pw.println("StrongAuth:");
+ pw.increaseIndent();
+ mStrongAuth.dump(pw);
+ pw.println();
+ pw.decreaseIndent();
}
/**
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 91cf53e..fbee6f4 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import android.app.AlarmManager;
@@ -32,8 +33,10 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
/**
@@ -42,6 +45,7 @@
public class LockSettingsStrongAuth {
private static final String TAG = "LockSettings";
+ private static final boolean DEBUG = false;
private static final int MSG_REQUIRE_STRONG_AUTH = 1;
private static final int MSG_REGISTER_TRACKER = 2;
@@ -49,15 +53,40 @@
private static final int MSG_REMOVE_USER = 4;
private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5;
private static final int MSG_NO_LONGER_REQUIRE_STRONG_AUTH = 6;
+ private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
+ private static final int MSG_STRONG_BIOMETRIC_UNLOCK = 8;
+ private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT = 9;
private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
"LockSettingsStrongAuth.timeoutForUser";
+ private static final String NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG =
+ "LockSettingsPrimaryAuth.nonStrongBiometricTimeoutForUser";
+ private static final String NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG =
+ "LockSettingsPrimaryAuth.nonStrongBiometricIdleTimeoutForUser";
+
+ /**
+ * Default and maximum timeout in milliseconds after which unlocking with weak auth times out,
+ * i.e. the user has to use a strong authentication method like password, PIN or pattern.
+ */
+ public static final long DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS = 24 * 60 * 60 * 1000; // 24h
+ public static final long DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS =
+ 4 * 60 * 60 * 1000; // 4h
private final RemoteCallbackList<IStrongAuthTracker> mTrackers = new RemoteCallbackList<>();
private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
+ private final SparseBooleanArray mIsNonStrongBiometricAllowedForUser = new SparseBooleanArray();
private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener>
mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>();
+ // Track non-strong biometric timeout
+ private final ArrayMap<Integer, NonStrongBiometricTimeoutAlarmListener>
+ mNonStrongBiometricTimeoutAlarmListener = new ArrayMap<>();
+ // Track non-strong biometric idle timeout
+ private final ArrayMap<Integer, NonStrongBiometricIdleTimeoutAlarmListener>
+ mNonStrongBiometricIdleTimeoutAlarmListener = new ArrayMap<>();
+
private final int mDefaultStrongAuthFlags;
+ private final boolean mDefaultIsNonStrongBiometricAllowed = true;
+
private final Context mContext;
private AlarmManager mAlarmManager;
@@ -80,6 +109,17 @@
Slog.e(TAG, "Exception while adding StrongAuthTracker.", e);
}
}
+
+ for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) {
+ int key = mIsNonStrongBiometricAllowedForUser.keyAt(i);
+ boolean value = mIsNonStrongBiometricAllowedForUser.valueAt(i);
+ try {
+ tracker.onIsNonStrongBiometricAllowedChanged(value, key);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while adding StrongAuthTracker: "
+ + "IsNonStrongBiometricAllowedChanged.", e);
+ }
+ }
}
private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
@@ -134,6 +174,13 @@
mStrongAuthForUser.removeAt(index);
notifyStrongAuthTrackers(mDefaultStrongAuthFlags, userId);
}
+
+ index = mIsNonStrongBiometricAllowedForUser.indexOfKey(userId);
+ if (index >= 0) {
+ mIsNonStrongBiometricAllowedForUser.removeAt(index);
+ notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(
+ mDefaultIsNonStrongBiometricAllowed, userId);
+ }
}
private void handleScheduleStrongAuthTimeout(int userId) {
@@ -151,6 +198,125 @@
// schedule a new alarm listener for the user
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
alarm, mHandler);
+
+ // cancel current non-strong biometric alarm listener for the user (if there was one)
+ cancelNonStrongBiometricAlarmListener(userId);
+ // cancel current non-strong biometric idle alarm listener for the user (if there was one)
+ cancelNonStrongBiometricIdleAlarmListener(userId);
+ // re-allow unlock with non-strong biometrics
+ setIsNonStrongBiometricAllowed(true, userId);
+ }
+
+ private void handleScheduleNonStrongBiometricTimeout(int userId) {
+ if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricTimeout for userId=" + userId);
+ long when = SystemClock.elapsedRealtime() + DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS;
+ NonStrongBiometricTimeoutAlarmListener alarm = mNonStrongBiometricTimeoutAlarmListener
+ .get(userId);
+ if (alarm != null) {
+ // Unlock with non-strong biometric will not affect the existing non-strong biometric
+ // timeout alarm
+ if (DEBUG) {
+ Slog.d(TAG, "There is an existing alarm for non-strong biometric"
+ + " fallback timeout, so do not re-schedule");
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Schedule a new alarm for non-strong biometric fallback timeout");
+ }
+ alarm = new NonStrongBiometricTimeoutAlarmListener(userId);
+ mNonStrongBiometricTimeoutAlarmListener.put(userId, alarm);
+ // schedule a new alarm listener for the user
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when,
+ NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG, alarm, mHandler);
+ }
+
+ // cancel current non-strong biometric idle alarm listener for the user (if there was one)
+ cancelNonStrongBiometricIdleAlarmListener(userId);
+ }
+
+ private void handleStrongBiometricUnlock(int userId) {
+ if (DEBUG) Slog.d(TAG, "handleStrongBiometricUnlock for userId=" + userId);
+ // cancel current non-strong biometric alarm listener for the user (if there was one)
+ cancelNonStrongBiometricAlarmListener(userId);
+ // cancel current non-strong biometric idle alarm listener for the user (if there was one)
+ cancelNonStrongBiometricIdleAlarmListener(userId);
+ // re-allow unlock with non-strong biometrics
+ setIsNonStrongBiometricAllowed(true, userId);
+ }
+
+ private void cancelNonStrongBiometricAlarmListener(int userId) {
+ if (DEBUG) Slog.d(TAG, "cancelNonStrongBiometricAlarmListener for userId=" + userId);
+ NonStrongBiometricTimeoutAlarmListener alarm = mNonStrongBiometricTimeoutAlarmListener
+ .get(userId);
+ if (alarm != null) {
+ if (DEBUG) Slog.d(TAG, "Cancel alarm for non-strong biometric fallback timeout");
+ mAlarmManager.cancel(alarm);
+ // need to remove the alarm when cancelled by primary auth or strong biometric
+ mNonStrongBiometricTimeoutAlarmListener.remove(userId);
+ }
+ }
+
+ private void cancelNonStrongBiometricIdleAlarmListener(int userId) {
+ if (DEBUG) Slog.d(TAG, "cancelNonStrongBiometricIdleAlarmListener for userId=" + userId);
+ // cancel idle alarm listener by any unlocks (i.e. primary auth, strong biometric,
+ // non-strong biometric)
+ NonStrongBiometricIdleTimeoutAlarmListener alarm =
+ mNonStrongBiometricIdleTimeoutAlarmListener.get(userId);
+ if (alarm != null) {
+ if (DEBUG) Slog.d(TAG, "Cancel alarm for non-strong biometric idle timeout");
+ mAlarmManager.cancel(alarm);
+ }
+ }
+
+ private void setIsNonStrongBiometricAllowed(boolean allowed, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "setIsNonStrongBiometricAllowed for allowed=" + allowed
+ + ", userId=" + userId);
+ }
+ if (userId == UserHandle.USER_ALL) {
+ for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) {
+ int key = mIsNonStrongBiometricAllowedForUser.keyAt(i);
+ setIsNonStrongBiometricAllowedOneUser(allowed, key);
+ }
+ } else {
+ setIsNonStrongBiometricAllowedOneUser(allowed, userId);
+ }
+ }
+
+ private void setIsNonStrongBiometricAllowedOneUser(boolean allowed, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "setIsNonStrongBiometricAllowedOneUser for allowed=" + allowed
+ + ", userId=" + userId);
+ }
+ boolean oldValue = mIsNonStrongBiometricAllowedForUser.get(userId,
+ mDefaultIsNonStrongBiometricAllowed);
+ if (allowed != oldValue) {
+ if (DEBUG) {
+ Slog.d(TAG, "mIsNonStrongBiometricAllowedForUser value changed:"
+ + " oldValue=" + oldValue + ", allowed=" + allowed);
+ }
+ mIsNonStrongBiometricAllowedForUser.put(userId, allowed);
+ notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(allowed, userId);
+ }
+ }
+
+ private void handleScheduleNonStrongBiometricIdleTimeout(int userId) {
+ if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricIdleTimeout for userId=" + userId);
+ long when = SystemClock.elapsedRealtime() + DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS;
+ // cancel current alarm listener for the user (if there was one)
+ NonStrongBiometricIdleTimeoutAlarmListener alarm =
+ mNonStrongBiometricIdleTimeoutAlarmListener.get(userId);
+ if (alarm != null) {
+ if (DEBUG) Slog.d(TAG, "Cancel existing alarm for non-strong biometric idle timeout");
+ mAlarmManager.cancel(alarm);
+ } else {
+ alarm = new NonStrongBiometricIdleTimeoutAlarmListener(userId);
+ mNonStrongBiometricIdleTimeoutAlarmListener.put(userId, alarm);
+ }
+ // schedule a new alarm listener for the user
+ if (DEBUG) Slog.d(TAG, "Schedule a new alarm for non-strong biometric idle timeout");
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when,
+ NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG, alarm, mHandler);
}
private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
@@ -170,6 +336,29 @@
}
}
+ private void notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(boolean allowed,
+ int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyStrongAuthTrackersForIsNonStrongBiometricAllowed"
+ + " for allowed=" + allowed + ", userId=" + userId);
+ }
+ int i = mTrackers.beginBroadcast();
+ try {
+ while (i > 0) {
+ i--;
+ try {
+ mTrackers.getBroadcastItem(i).onIsNonStrongBiometricAllowedChanged(
+ allowed, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while notifying StrongAuthTracker: "
+ + "IsNonStrongBiometricAllowedChanged.", e);
+ }
+ }
+ } finally {
+ mTrackers.finishBroadcast();
+ }
+ }
+
public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget();
}
@@ -207,11 +396,45 @@
requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
}
+ /**
+ * Report successful unlocking with primary auth
+ */
public void reportSuccessfulStrongAuthUnlock(int userId) {
final int argNotUsed = 0;
mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
}
+ /**
+ * Report successful unlocking with biometric
+ */
+ public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "reportSuccessfulBiometricUnlock for isStrongBiometric="
+ + isStrongBiometric + ", userId=" + userId);
+ }
+ final int argNotUsed = 0;
+ if (isStrongBiometric) { // unlock with strong biometric
+ mHandler.obtainMessage(MSG_STRONG_BIOMETRIC_UNLOCK, userId, argNotUsed)
+ .sendToTarget();
+ } else { // unlock with non-strong biometric (i.e. weak or convenience)
+ mHandler.obtainMessage(MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT, userId, argNotUsed)
+ .sendToTarget();
+ }
+ }
+
+ /**
+ * Schedule idle timeout for non-strong biometric (i.e. weak or convenience)
+ */
+ public void scheduleNonStrongBiometricIdleTimeout(int userId) {
+ if (DEBUG) Slog.d(TAG, "scheduleNonStrongBiometricIdleTimeout for userId=" + userId);
+ final int argNotUsed = 0;
+ mHandler.obtainMessage(MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT, userId, argNotUsed)
+ .sendToTarget();
+ }
+
+ /**
+ * Alarm of fallback timeout for primary auth
+ */
private class StrongAuthTimeoutAlarmListener implements OnAlarmListener {
private final int mUserId;
@@ -226,6 +449,41 @@
}
}
+ /**
+ * Alarm of fallback timeout for non-strong biometric (i.e. weak or convenience)
+ */
+ private class NonStrongBiometricTimeoutAlarmListener implements OnAlarmListener {
+
+ private final int mUserId;
+
+ NonStrongBiometricTimeoutAlarmListener(int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void onAlarm() {
+ requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, mUserId);
+ }
+ }
+
+ /**
+ * Alarm of idle timeout for non-strong biometric (i.e. weak or convenience biometric)
+ */
+ private class NonStrongBiometricIdleTimeoutAlarmListener implements OnAlarmListener {
+
+ private final int mUserId;
+
+ NonStrongBiometricIdleTimeoutAlarmListener(int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void onAlarm() {
+ // disallow unlock with non-strong biometrics
+ setIsNonStrongBiometricAllowed(false, mUserId);
+ }
+ }
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -248,8 +506,38 @@
case MSG_NO_LONGER_REQUIRE_STRONG_AUTH:
handleNoLongerRequireStrongAuth(msg.arg1, msg.arg2);
break;
+ case MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT:
+ handleScheduleNonStrongBiometricTimeout(msg.arg1);
+ break;
+ case MSG_STRONG_BIOMETRIC_UNLOCK:
+ handleStrongBiometricUnlock(msg.arg1);
+ break;
+ case MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT:
+ handleScheduleNonStrongBiometricIdleTimeout(msg.arg1);
+ break;
}
}
};
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("PrimaryAuthFlags state:");
+ pw.increaseIndent();
+ for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+ final int key = mStrongAuthForUser.keyAt(i);
+ final int value = mStrongAuthForUser.valueAt(i);
+ pw.println("userId=" + key + ", primaryAuthFlags=" + Integer.toHexString(value));
+ }
+ pw.println();
+ pw.decreaseIndent();
+
+ pw.println("NonStrongBiometricAllowed state:");
+ pw.increaseIndent();
+ for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) {
+ final int key = mIsNonStrongBiometricAllowedForUser.keyAt(i);
+ final boolean value = mIsNonStrongBiometricAllowedForUser.valueAt(i);
+ pw.println("userId=" + key + ", allowed=" + value);
+ }
+ pw.println();
+ pw.decreaseIndent();
+ }
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 33e01bd..265b9ad 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -213,7 +213,8 @@
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
.setDescription(mContext.getResources().getText(
R.string.bluetooth_a2dp_audio_route_name).toString())
- .setDeviceType(MediaRoute2Info.DEVICE_TYPE_BLUETOOTH)
+ //TODO: Set type correctly (BLUETOOTH_A2DP or HEARING_AID)
+ .setType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)
.setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.build();
newBtRoute.connectedProfiles = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 9b1824f..f144405 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -53,15 +53,15 @@
public abstract void requestCreateSession(String packageName, String routeId, long requestId,
@Nullable Bundle sessionHints);
- public abstract void releaseSession(String sessionId);
+ public abstract void releaseSession(String sessionId, long requestId);
public abstract void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference);
- public abstract void selectRoute(String sessionId, String routeId);
- public abstract void deselectRoute(String sessionId, String routeId);
- public abstract void transferToRoute(String sessionId, String routeId);
+ public abstract void selectRoute(String sessionId, String routeId, long requestId);
+ public abstract void deselectRoute(String sessionId, String routeId, long requestId);
+ public abstract void transferToRoute(String sessionId, String routeId, long requestId);
- public abstract void setRouteVolume(String routeId, int volume);
- public abstract void setSessionVolume(String sessionId, int volume);
+ public abstract void setRouteVolume(String routeId, int volume, long requestId);
+ public abstract void setSessionVolume(String sessionId, int volume, long requestId);
@NonNull
public String getUniqueId() {
@@ -116,5 +116,6 @@
@NonNull RoutingSessionInfo sessionInfo);
void onSessionReleased(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo);
+ void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, int reason);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 60934e0..e64776c 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -87,9 +87,9 @@
}
@Override
- public void releaseSession(String sessionId) {
+ public void releaseSession(String sessionId, long requestId) {
if (mConnectionReady) {
- mActiveConnection.releaseSession(sessionId);
+ mActiveConnection.releaseSession(sessionId, requestId);
updateBinding();
}
}
@@ -103,38 +103,38 @@
}
@Override
- public void selectRoute(String sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId, long requestId) {
if (mConnectionReady) {
- mActiveConnection.selectRoute(sessionId, routeId);
+ mActiveConnection.selectRoute(sessionId, routeId, requestId);
}
}
@Override
- public void deselectRoute(String sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId, long requestId) {
if (mConnectionReady) {
- mActiveConnection.deselectRoute(sessionId, routeId);
+ mActiveConnection.deselectRoute(sessionId, routeId, requestId);
}
}
@Override
- public void transferToRoute(String sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId, long requestId) {
if (mConnectionReady) {
- mActiveConnection.transferToRoute(sessionId, routeId);
+ mActiveConnection.transferToRoute(sessionId, routeId, requestId);
}
}
@Override
- public void setRouteVolume(String routeId, int volume) {
+ public void setRouteVolume(String routeId, int volume, long requestId) {
if (mConnectionReady) {
- mActiveConnection.setRouteVolume(routeId, volume);
+ mActiveConnection.setRouteVolume(routeId, volume, requestId);
updateBinding();
}
}
@Override
- public void setSessionVolume(String sessionId, int volume) {
+ public void setSessionVolume(String sessionId, int volume, long requestId) {
if (mConnectionReady) {
- mActiveConnection.setSessionVolume(sessionId, volume);
+ mActiveConnection.setSessionVolume(sessionId, volume, requestId);
updateBinding();
}
}
@@ -333,8 +333,8 @@
return;
}
- if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) {
- Slog.w(TAG, "onSessionCreationFailed: Ignoring requestId REQUEST_ID_UNKNOWN");
+ if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) {
+ Slog.w(TAG, "onSessionCreationFailed: Ignoring requestId REQUEST_ID_NONE");
return;
}
@@ -406,6 +406,19 @@
mCallback.onSessionReleased(this, sessionInfo);
}
+ private void onRequestFailed(Connection connection, long requestId, int reason) {
+ if (mActiveConnection != connection) {
+ return;
+ }
+
+ if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) {
+ Slog.w(TAG, "onRequestFailed: Ignoring requestId REQUEST_ID_NONE");
+ return;
+ }
+
+ mCallback.onRequestFailed(this, requestId, reason);
+ }
+
private void disconnect() {
if (mActiveConnection != null) {
mConnectionReady = false;
@@ -461,9 +474,9 @@
}
}
- public void releaseSession(String sessionId) {
+ public void releaseSession(String sessionId, long requestId) {
try {
- mService.releaseSession(sessionId);
+ mService.releaseSession(sessionId, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "releaseSession: Failed to deliver request.");
}
@@ -477,41 +490,41 @@
}
}
- public void selectRoute(String sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId, long requestId) {
try {
- mService.selectRoute(sessionId, routeId);
+ mService.selectRoute(sessionId, routeId, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "selectRoute: Failed to deliver request.", ex);
}
}
- public void deselectRoute(String sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId, long requestId) {
try {
- mService.deselectRoute(sessionId, routeId);
+ mService.deselectRoute(sessionId, routeId, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "deselectRoute: Failed to deliver request.", ex);
}
}
- public void transferToRoute(String sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId, long requestId) {
try {
- mService.transferToRoute(sessionId, routeId);
+ mService.transferToRoute(sessionId, routeId, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "transferToRoute: Failed to deliver request.", ex);
}
}
- public void setRouteVolume(String routeId, int volume) {
+ public void setRouteVolume(String routeId, int volume, long requestId) {
try {
- mService.setRouteVolume(routeId, volume);
+ mService.setRouteVolume(routeId, volume, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "setRouteVolume: Failed to deliver request.", ex);
}
}
- public void setSessionVolume(String sessionId, int volume) {
+ public void setSessionVolume(String sessionId, int volume, long requestId) {
try {
- mService.setSessionVolume(sessionId, volume);
+ mService.setSessionVolume(sessionId, volume, requestId);
} catch (RemoteException ex) {
Slog.e(TAG, "setSessionVolume: Failed to deliver request.", ex);
}
@@ -541,6 +554,10 @@
void postSessionReleased(RoutingSessionInfo sessionInfo) {
mHandler.post(() -> onSessionReleased(Connection.this, sessionInfo));
}
+
+ void postSessionReleased(long requestId, int reason) {
+ mHandler.post(() -> onRequestFailed(Connection.this, requestId, reason));
+ }
}
private static final class ServiceCallbackStub extends
@@ -594,5 +611,13 @@
connection.postSessionReleased(sessionInfo);
}
}
+
+ @Override
+ public void notifyRequestFailed(long requestId, int reason) {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postSessionReleased(requestId, reason);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 3588916..e78a35c 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -16,6 +16,7 @@
package com.android.server.media;
+import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static android.media.MediaRouter2Utils.getOriginalId;
import static android.media.MediaRouter2Utils.getProviderId;
@@ -30,7 +31,6 @@
import android.media.IMediaRouter2Manager;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
-import android.media.MediaRoute2ProviderService;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Binder;
@@ -70,6 +70,12 @@
private static final String TAG = "MR2ServiceImpl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ /**
+ * TODO: Change this with the real request ID from MediaRouter2 when
+ * MediaRouter2 needs to get notified for the failures.
+ */
+ private static final long DUMMY_REQUEST_ID = -1;
+
private final Context mContext;
private final Object mLock = new Object();
final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1);
@@ -365,14 +371,14 @@
}
public void setRouteVolumeWithManager(IMediaRouter2Manager manager,
- MediaRoute2Info route, int volume) {
+ MediaRoute2Info route, int volume, int requestId) {
Objects.requireNonNull(manager, "manager must not be null");
Objects.requireNonNull(route, "route must not be null");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- setRouteVolumeWithManagerLocked(manager, route, volume);
+ setRouteVolumeWithManagerLocked(manager, route, volume, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -397,7 +403,7 @@
}
public void selectRouteWithManager(IMediaRouter2Manager manager, String uniqueSessionId,
- MediaRoute2Info route) {
+ MediaRoute2Info route, int requestId) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
@@ -407,7 +413,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- selectRouteWithManagerLocked(manager, uniqueSessionId, route);
+ selectRouteWithManagerLocked(manager, uniqueSessionId, route, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -415,7 +421,7 @@
}
public void deselectRouteWithManager(IMediaRouter2Manager manager, String uniqueSessionId,
- MediaRoute2Info route) {
+ MediaRoute2Info route, int requestId) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
@@ -425,7 +431,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- deselectRouteWithManagerLocked(manager, uniqueSessionId, route);
+ deselectRouteWithManagerLocked(manager, uniqueSessionId, route, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -433,7 +439,7 @@
}
public void transferToRouteWithManager(IMediaRouter2Manager manager, String uniqueSessionId,
- MediaRoute2Info route) {
+ MediaRoute2Info route, int requestId) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
@@ -443,7 +449,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- transferToRouteWithManagerLocked(manager, uniqueSessionId, route);
+ transferToRouteWithManagerLocked(manager, uniqueSessionId, route, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -451,7 +457,7 @@
}
public void setSessionVolumeWithManager(IMediaRouter2Manager manager,
- String uniqueSessionId, int volume) {
+ String uniqueSessionId, int volume, int requestId) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
@@ -460,14 +466,15 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- setSessionVolumeWithManagerLocked(manager, uniqueSessionId, volume);
+ setSessionVolumeWithManagerLocked(manager, uniqueSessionId, volume, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
- public void releaseSessionWithManager(IMediaRouter2Manager manager, String uniqueSessionId) {
+ public void releaseSessionWithManager(IMediaRouter2Manager manager, String uniqueSessionId,
+ int requestId) {
Objects.requireNonNull(manager, "manager must not be null");
if (TextUtils.isEmpty(uniqueSessionId)) {
throw new IllegalArgumentException("uniqueSessionId must not be empty");
@@ -476,7 +483,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- releaseSessionWithManagerLocked(manager, uniqueSessionId);
+ releaseSessionWithManagerLocked(manager, uniqueSessionId, requestId);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -572,7 +579,7 @@
obtainMessage(UserHandler::notifyPreferredFeaturesChangedToManagers,
routerRecord.mUserRecord.mHandler, routerRecord));
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::updateDiscoveryPreference,
+ obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
routerRecord.mUserRecord.mHandler));
}
@@ -584,7 +591,7 @@
if (routerRecord != null) {
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::setRouteVolumeOnHandler,
- routerRecord.mUserRecord.mHandler, route, volume));
+ routerRecord.mUserRecord.mHandler, route, volume, DUMMY_REQUEST_ID));
}
}
@@ -615,7 +622,8 @@
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::selectRouteOnHandler,
- routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route));
+ routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route,
+ DUMMY_REQUEST_ID));
}
private void deselectRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
@@ -629,7 +637,8 @@
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::deselectRouteOnHandler,
- routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route));
+ routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route,
+ DUMMY_REQUEST_ID));
}
private void transferToRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
@@ -643,7 +652,8 @@
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::transferToRouteOnHandler,
- routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route));
+ routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route,
+ DUMMY_REQUEST_ID));
}
private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router,
@@ -657,7 +667,8 @@
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::setSessionVolumeOnHandler,
- routerRecord.mUserRecord.mHandler, uniqueSessionId, volume));
+ routerRecord.mUserRecord.mHandler, uniqueSessionId, volume,
+ DUMMY_REQUEST_ID));
}
private void releaseSessionWithRouter2Locked(@NonNull IMediaRouter2 router,
@@ -671,7 +682,8 @@
routerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::releaseSessionOnHandler,
- routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId));
+ routerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId,
+ DUMMY_REQUEST_ID));
}
////////////////////////////////////////////////////////////
@@ -744,16 +756,18 @@
}
private void setRouteVolumeWithManagerLocked(@NonNull IMediaRouter2Manager manager,
- @NonNull MediaRoute2Info route, int volume) {
+ @NonNull MediaRoute2Info route, int volume, int requestId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
if (managerRecord == null) {
return;
}
+
+ long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::setRouteVolumeOnHandler,
- managerRecord.mUserRecord.mHandler, route, volume));
+ managerRecord.mUserRecord.mHandler, route, volume, uniqueRequestId));
}
private void requestCreateSessionWithManagerLocked(@NonNull IMediaRouter2Manager manager,
@@ -778,7 +792,7 @@
}
private void selectRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, int requestId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -790,13 +804,15 @@
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
+ long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::selectRouteOnHandler,
- managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route));
+ managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route,
+ uniqueRequestId));
}
private void deselectRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, int requestId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -808,13 +824,15 @@
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
+ long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::deselectRouteOnHandler,
- managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route));
+ managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route,
+ uniqueRequestId));
}
private void transferToRouteWithManagerLocked(@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, int requestId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -826,13 +844,15 @@
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
+ long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::transferToRouteOnHandler,
- managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route));
+ managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId, route,
+ uniqueRequestId));
}
private void setSessionVolumeWithManagerLocked(@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId, int volume) {
+ @NonNull String uniqueSessionId, int volume, int requestId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -840,13 +860,15 @@
return;
}
+ long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::setSessionVolumeOnHandler,
- managerRecord.mUserRecord.mHandler, uniqueSessionId, volume));
+ managerRecord.mUserRecord.mHandler, uniqueSessionId, volume,
+ uniqueRequestId));
}
private void releaseSessionWithManagerLocked(@NonNull IMediaRouter2Manager manager,
- @NonNull String uniqueSessionId) {
+ @NonNull String uniqueSessionId, int requestId) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -856,10 +878,15 @@
RouterRecord routerRecord = managerRecord.mUserRecord.mHandler
.findRouterforSessionLocked(uniqueSessionId);
+ if (routerRecord == null) {
+ return;
+ }
+ long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
managerRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::releaseSessionOnHandler,
- managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId));
+ managerRecord.mUserRecord.mHandler, routerRecord, uniqueSessionId,
+ uniqueRequestId));
}
////////////////////////////////////////////////////////////
@@ -1100,6 +1127,13 @@
this, provider, sessionInfo));
}
+ @Override
+ public void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId,
+ int reason) {
+ sendMessage(PooledLambda.obtainMessage(UserHandler::onRequestFailedOnHandler,
+ this, provider, requestId, reason));
+ }
+
@Nullable
public RouterRecord findRouterforSessionLocked(@NonNull String uniqueSessionId) {
return mSessionToRouterMap.get(uniqueSessionId);
@@ -1195,7 +1229,7 @@
if (provider == null) {
Slog.w(TAG, "Ignoring session creation request since no provider found for"
+ " given route=" + route);
- notifySessionCreationFailed(routerRecord, toOriginalRequestId(requestId));
+ notifySessionCreationFailedToRouter(routerRecord, toOriginalRequestId(requestId));
return;
}
@@ -1210,7 +1244,7 @@
// routerRecord can be null if the session is system's.
private void selectRouteOnHandler(@Nullable RouterRecord routerRecord,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, long requestId) {
if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
"selecting")) {
return;
@@ -1222,12 +1256,12 @@
if (provider == null) {
return;
}
- provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
+ provider.selectRoute(getOriginalId(uniqueSessionId), route.getOriginalId(), requestId);
}
// routerRecord can be null if the session is system's.
private void deselectRouteOnHandler(@Nullable RouterRecord routerRecord,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, long requestId) {
if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
"deselecting")) {
return;
@@ -1239,12 +1273,13 @@
if (provider == null) {
return;
}
- provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
+ provider.deselectRoute(getOriginalId(uniqueSessionId), route.getOriginalId(),
+ requestId);
}
// routerRecord can be null if the session is system's.
private void transferToRouteOnHandler(@Nullable RouterRecord routerRecord,
- @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
+ @NonNull String uniqueSessionId, @NonNull MediaRoute2Info route, long requestId) {
if (!checkArgumentsForSessionControl(routerRecord, uniqueSessionId, route,
"transferring to")) {
return;
@@ -1256,7 +1291,8 @@
if (provider == null) {
return;
}
- provider.transferToRoute(getOriginalId(uniqueSessionId), route.getOriginalId());
+ provider.transferToRoute(getOriginalId(uniqueSessionId), route.getOriginalId(),
+ requestId);
}
private boolean checkArgumentsForSessionControl(@Nullable RouterRecord routerRecord,
@@ -1299,7 +1335,7 @@
}
private void releaseSessionOnHandler(@NonNull RouterRecord routerRecord,
- @NonNull String uniqueSessionId) {
+ @NonNull String uniqueSessionId, long uniqueRequestId) {
final RouterRecord matchingRecord = mSessionToRouterMap.get(uniqueSessionId);
if (matchingRecord != routerRecord) {
Slog.w(TAG, "Ignoring releasing session from non-matching router."
@@ -1329,14 +1365,14 @@
return;
}
- provider.releaseSession(sessionId);
+ provider.releaseSession(sessionId, uniqueRequestId);
}
private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo, long requestId) {
notifySessionCreatedToManagers(getManagers(), sessionInfo);
- if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) {
+ if (requestId == REQUEST_ID_NONE) {
// The session is created without any matching request.
return;
}
@@ -1362,7 +1398,7 @@
if (sessionInfo == null) {
// Failed
- notifySessionCreationFailed(matchingRequest.mRouterRecord,
+ notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord,
toOriginalRequestId(requestId));
return;
}
@@ -1374,13 +1410,13 @@
Slog.w(TAG, "Created session doesn't match the original request."
+ " originalRouteId=" + originalRouteId
+ ", requestId=" + requestId + ", sessionInfo=" + sessionInfo);
- notifySessionCreationFailed(matchingRequest.mRouterRecord,
+ notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord,
toOriginalRequestId(requestId));
return;
}
// Succeeded
- notifySessionCreated(matchingRequest.mRouterRecord,
+ notifySessionCreatedToRouter(matchingRequest.mRouterRecord,
sessionInfo, toOriginalRequestId(requestId));
mSessionToRouterMap.put(sessionInfo.getId(), routerRecord);
}
@@ -1405,7 +1441,7 @@
}
mSessionCreationRequests.remove(matchingRequest);
- notifySessionCreationFailed(matchingRequest.mRouterRecord,
+ notifySessionCreationFailedToRouter(matchingRequest.mRouterRecord,
toOriginalRequestId(requestId));
}
@@ -1429,7 +1465,7 @@
Slog.w(TAG, "No matching router found for session=" + sessionInfo);
return;
}
- notifySessionInfoChanged(routerRecord, sessionInfo);
+ notifySessionInfoChangedToRouter(routerRecord, sessionInfo);
}
private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -1442,10 +1478,38 @@
Slog.w(TAG, "No matching router found for session=" + sessionInfo);
return;
}
- notifySessionReleased(routerRecord, sessionInfo);
+ notifySessionReleasedToRouter(routerRecord, sessionInfo);
}
- private void notifySessionCreated(@NonNull RouterRecord routerRecord,
+ private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider,
+ long requestId, int reason) {
+ final int managerId = toRouterOrManagerId(requestId);
+
+ MediaRouter2ServiceImpl service = mServiceRef.get();
+ if (service == null) {
+ return;
+ }
+
+ ManagerRecord managerToNotifyFailure = null;
+ synchronized (service.mLock) {
+ for (ManagerRecord manager : mUserRecord.mManagerRecords) {
+ if (manager.mManagerId == managerId) {
+ managerToNotifyFailure = manager;
+ break;
+ }
+ }
+ }
+
+ if (managerToNotifyFailure == null) {
+ Slog.w(TAG, "No matching managerRecord found for managerId=" + managerId);
+ return;
+ }
+
+ notifyRequestFailedToManager(
+ managerToNotifyFailure.mManager, toOriginalRequestId(requestId), reason);
+ }
+
+ private void notifySessionCreatedToRouter(@NonNull RouterRecord routerRecord,
@NonNull RoutingSessionInfo sessionInfo, int requestId) {
try {
routerRecord.mRouter.notifySessionCreated(sessionInfo, requestId);
@@ -1455,7 +1519,7 @@
}
}
- private void notifySessionCreationFailed(@NonNull RouterRecord routerRecord,
+ private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
int requestId) {
try {
routerRecord.mRouter.notifySessionCreated(/* sessionInfo= */ null, requestId);
@@ -1465,7 +1529,7 @@
}
}
- private void notifySessionInfoChanged(@NonNull RouterRecord routerRecord,
+ private void notifySessionInfoChangedToRouter(@NonNull RouterRecord routerRecord,
@NonNull RoutingSessionInfo sessionInfo) {
try {
routerRecord.mRouter.notifySessionInfoChanged(sessionInfo);
@@ -1475,7 +1539,7 @@
}
}
- private void notifySessionReleased(@NonNull RouterRecord routerRecord,
+ private void notifySessionReleasedToRouter(@NonNull RouterRecord routerRecord,
@NonNull RoutingSessionInfo sessionInfo) {
try {
routerRecord.mRouter.notifySessionReleased(sessionInfo);
@@ -1485,21 +1549,23 @@
}
}
- private void setRouteVolumeOnHandler(@NonNull MediaRoute2Info route, int volume) {
+ private void setRouteVolumeOnHandler(@NonNull MediaRoute2Info route, int volume,
+ long requestId) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider != null) {
- provider.setRouteVolume(route.getOriginalId(), volume);
+ provider.setRouteVolume(route.getOriginalId(), volume, requestId);
}
}
- private void setSessionVolumeOnHandler(@NonNull String uniqueSessionId, int volume) {
+ private void setSessionVolumeOnHandler(@NonNull String uniqueSessionId, int volume,
+ long requestId) {
final MediaRoute2Provider provider = findProvider(getProviderId(uniqueSessionId));
if (provider == null) {
Slog.w(TAG, "setSessionVolume: couldn't find provider for session "
+ "id=" + uniqueSessionId);
return;
}
- provider.setSessionVolume(getOriginalId(uniqueSessionId), volume);
+ provider.setSessionVolume(getOriginalId(uniqueSessionId), volume, requestId);
}
private List<IMediaRouter2> getRouters() {
@@ -1683,7 +1749,17 @@
}
}
- private void updateDiscoveryPreference() {
+ private void notifyRequestFailedToManager(@NonNull IMediaRouter2Manager manager,
+ int requestId, int reason) {
+ try {
+ manager.notifyRequestFailed(requestId, reason);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify manager of the request failure."
+ + " Manager probably died.", ex);
+ }
+ }
+
+ private void updateDiscoveryPreferenceOnHandler() {
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
return;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 580fc52..a13ee10 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -543,8 +543,8 @@
// Binder call
@Override
public void setRouteVolumeWithManager(IMediaRouter2Manager manager,
- MediaRoute2Info route, int volume) {
- mService2.setRouteVolumeWithManager(manager, route, volume);
+ MediaRoute2Info route, int volume, int requestId) {
+ mService2.setRouteVolumeWithManager(manager, route, volume, requestId);
}
// Binder call
@@ -557,35 +557,36 @@
// Binder call
@Override
public void selectRouteWithManager(IMediaRouter2Manager manager, String sessionId,
- MediaRoute2Info route) {
- mService2.selectRouteWithManager(manager, sessionId, route);
+ MediaRoute2Info route, int requestId) {
+ mService2.selectRouteWithManager(manager, sessionId, route, requestId);
}
// Binder call
@Override
public void deselectRouteWithManager(IMediaRouter2Manager manager, String sessionId,
- MediaRoute2Info route) {
- mService2.deselectRouteWithManager(manager, sessionId, route);
+ MediaRoute2Info route, int requestId) {
+ mService2.deselectRouteWithManager(manager, sessionId, route, requestId);
}
// Binder call
@Override
public void transferToRouteWithManager(IMediaRouter2Manager manager, String sessionId,
- MediaRoute2Info route) {
- mService2.transferToRouteWithManager(manager, sessionId, route);
+ MediaRoute2Info route, int requestId) {
+ mService2.transferToRouteWithManager(manager, sessionId, route, requestId);
}
// Binder call
@Override
public void setSessionVolumeWithManager(IMediaRouter2Manager manager,
- String sessionId, int volume) {
- mService2.setSessionVolumeWithManager(manager, sessionId, volume);
+ String sessionId, int volume, int requestId) {
+ mService2.setSessionVolumeWithManager(manager, sessionId, volume, requestId);
}
// Binder call
@Override
- public void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId) {
- mService2.releaseSessionWithManager(manager, sessionId);
+ public void releaseSessionWithManager(IMediaRouter2Manager manager, String sessionId,
+ int requestId) {
+ mService2.releaseSessionWithManager(manager, sessionId, requestId);
}
void restoreBluetoothA2dp() {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index f7e1398..da9c27e 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -18,6 +18,7 @@
import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -130,26 +131,27 @@
}
@Override
- public void releaseSession(String sessionId) {
+ public void releaseSession(String sessionId, long requestId) {
// Do nothing
}
+
@Override
public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
// Do nothing
}
@Override
- public void selectRoute(String sessionId, String routeId) {
+ public void selectRoute(String sessionId, String routeId, long requestId) {
// Do nothing since we don't support multiple BT yet.
}
@Override
- public void deselectRoute(String sessionId, String routeId) {
+ public void deselectRoute(String sessionId, String routeId, long requestId) {
// Do nothing since we don't support multiple BT yet.
}
@Override
- public void transferToRoute(String sessionId, String routeId) {
+ public void transferToRoute(String sessionId, String routeId, long requestId) {
if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
mBtRouteProvider.transferTo(null);
} else {
@@ -158,7 +160,7 @@
}
@Override
- public void setRouteVolume(String routeId, int volume) {
+ public void setRouteVolume(String routeId, int volume, long requestId) {
if (!TextUtils.equals(routeId, mSelectedRouteId)) {
return;
}
@@ -166,7 +168,7 @@
}
@Override
- public void setSessionVolume(String sessionId, int volume) {
+ public void setSessionVolume(String sessionId, int volume, long requestId) {
// Do nothing since we don't support grouping volume yet.
}
@@ -192,6 +194,8 @@
: MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
.setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
+ //TODO: Guess the exact type using AudioDevice
+ .setType(TYPE_BUILTIN_SPEAKER)
.addFeature(FEATURE_LIVE_AUDIO)
.addFeature(FEATURE_LIVE_VIDEO)
.setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0662400..ee5a4fe 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1253,7 +1253,7 @@
final NetworkStats uidSnapshot = getNetworkStatsUidDetail(INTERFACES_ALL);
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotXt");
- final NetworkStats xtSnapshot = getNetworkStatsXt();
+ final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotDev");
final NetworkStats devSnapshot = readNetworkStatsSummaryDev();
@@ -1742,18 +1742,6 @@
mUseBpfTrafficStats);
uidSnapshot.combineAllValues(tetherSnapshot);
- final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
-
- // fold video calling data usage stats into uid snapshot
- final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
- if (vtStats != null) {
- vtStats.filter(UID_ALL, ifaces, TAG_ALL);
- mStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
- mUseBpfTrafficStats);
- uidSnapshot.combineAllValues(vtStats);
- }
-
// get a stale copy of uid stats snapshot provided by providers.
final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
providerStats.filter(UID_ALL, ifaces, TAG_ALL);
@@ -1766,24 +1754,6 @@
}
/**
- * Return snapshot of current XT statistics with video calling data usage statistics.
- */
- private NetworkStats getNetworkStatsXt() throws RemoteException {
- final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
-
- final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
-
- // Merge video calling data usage into XT
- final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(STATS_PER_IFACE);
- if (vtSnapshot != null) {
- xtSnapshot.combineAllValues(vtSnapshot);
- }
-
- return xtSnapshot;
- }
-
- /**
* Return snapshot of current tethering statistics. Will return empty
* {@link NetworkStats} if any problems are encountered.
*/
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index fce10e6..c301cd2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -27,4 +27,6 @@
String tag, int id, int userId);
void removeForegroundServiceFlagFromNotification(String pkg, int notificationId, int userId);
+
+ void onConversationRemoved(String pkg, int uid, String conversationId);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0d402e5..475f229 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -772,7 +772,7 @@
parser, mAllowedManagedServicePackages, forRestore, userId);
migratedManagedServices = true;
} else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {
- mSnoozeHelper.readXml(parser);
+ mSnoozeHelper.readXml(parser, System.currentTimeMillis());
}
if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
if (forRestore && userId != UserHandle.USER_SYSTEM) {
@@ -2322,6 +2322,8 @@
mConditionProviders.onBootPhaseAppsCanStart();
mHistoryManager.onBootPhaseAppsCanStart();
registerDeviceConfigChange();
+ } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
}
}
@@ -5474,6 +5476,11 @@
});
}
+ @Override
+ public void onConversationRemoved(String pkg, int uid, String conversationId) {
+ onConversationRemovedInternal(pkg, uid, conversationId);
+ }
+
@GuardedBy("mNotificationLock")
private void removeForegroundServiceFlagLocked(NotificationRecord r) {
if (r == null) {
@@ -5676,7 +5683,7 @@
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
}
- public void onConversationRemoved(String pkg, int uid, String conversationId) {
+ private void onConversationRemovedInternal(String pkg, int uid, String conversationId) {
checkCallerIsSystem();
Preconditions.checkStringNotEmpty(pkg);
Preconditions.checkStringNotEmpty(conversationId);
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index bae1dd3..d60c291 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -43,9 +44,7 @@
import java.io.IOException;
import java.io.PrintWriter;
-import java.sql.Array;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -172,7 +171,7 @@
for (int i = 0; i < allRecords.size(); i++) {
NotificationRecord r = allRecords.valueAt(i);
String currentGroupKey = r.getSbn().getGroup();
- if (currentGroupKey.equals(groupKey)) {
+ if (Objects.equals(currentGroupKey, groupKey)) {
records.add(r);
}
}
@@ -217,7 +216,7 @@
snooze(record);
scheduleRepost(pkg, key, userId, duration);
- Long activateAt = System.currentTimeMillis() + duration;
+ Long activateAt = SystemClock.elapsedRealtime() + duration;
synchronized (mPersistedSnoozedNotifications) {
storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
}
@@ -244,8 +243,6 @@
}
storeRecord(record.getSbn().getPackageName(), record.getKey(),
userId, mSnoozedNotifications, record);
- mPackages.put(record.getKey(), record.getSbn().getPackageName());
- mUsers.put(record.getKey(), userId);
}
private <T> void storeRecord(String pkg, String key, Integer userId,
@@ -258,6 +255,8 @@
keyToValue.put(key, object);
targets.put(getPkgKey(userId, pkg), keyToValue);
+ mPackages.put(key, pkg);
+ mUsers.put(key, userId);
}
private <T> T removeRecord(String pkg, String key, Integer userId,
@@ -425,12 +424,34 @@
PendingIntent.FLAG_UPDATE_CURRENT);
}
+ public void scheduleRepostsForPersistedNotifications(long currentTime) {
+ for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) {
+ for (int i = 0; i < snoozed.size(); i++) {
+ String key = snoozed.keyAt(i);
+ Long time = snoozed.valueAt(i);
+ String pkg = mPackages.get(key);
+ Integer userId = mUsers.get(key);
+ if (time == null || pkg == null || userId == null) {
+ Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId);
+ continue;
+ }
+ if (time != null && time > currentTime) {
+ scheduleRepostAtTime(pkg, key, userId, time);
+ }
+ }
+
+ }
+ }
+
private void scheduleRepost(String pkg, String key, int userId, long duration) {
+ scheduleRepostAtTime(pkg, key, userId, SystemClock.elapsedRealtime() + duration);
+ }
+
+ private void scheduleRepostAtTime(String pkg, String key, int userId, long time) {
long identity = Binder.clearCallingIdentity();
try {
final PendingIntent pi = createPendingIntent(pkg, key, userId);
mAm.cancel(pi);
- long time = SystemClock.elapsedRealtime() + duration;
if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time));
mAm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pi);
} finally {
@@ -496,6 +517,7 @@
private interface Inserter<T> {
void insert(T t) throws IOException;
}
+
private <T> void writeXml(XmlSerializer out,
ArrayMap<String, ArrayMap<String, T>> targets, String tag,
Inserter<T> attributeInserter)
@@ -503,12 +525,13 @@
synchronized (targets) {
final int M = targets.size();
for (int i = 0; i < M; i++) {
- String userIdPkgKey = targets.keyAt(i);
// T is a String (snoozed until context) or Long (snoozed until time)
ArrayMap<String, T> keyToValue = targets.valueAt(i);
for (int j = 0; j < keyToValue.size(); j++) {
String key = keyToValue.keyAt(j);
T value = keyToValue.valueAt(j);
+ String pkg = mPackages.get(key);
+ Integer userId = mUsers.get(key);
out.startTag(null, tag);
@@ -518,8 +541,7 @@
XML_SNOOZED_NOTIFICATION_VERSION);
out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
- String pkg = mPackages.get(key);
- int userId = mUsers.get(key);
+
out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
String.valueOf(userId));
@@ -530,7 +552,7 @@
}
}
- protected void readXml(XmlPullParser parser)
+ protected void readXml(XmlPullParser parser, long currentTime)
throws XmlPullParserException, IOException {
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -547,16 +569,15 @@
try {
final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
- final int userId = Integer.parseInt(
- parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID));
+ final int userId = XmlUtils.readIntAttribute(
+ parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
- final Long time = Long.parseLong(
- parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME));
- if (time > System.currentTimeMillis()) { //only read new stuff
+ final Long time = XmlUtils.readLongAttribute(
+ parser, XML_SNOOZED_NOTIFICATION_TIME, 0);
+ if (time > currentTime) { //only read new stuff
synchronized (mPersistedSnoozedNotifications) {
storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time);
}
- scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
}
}
if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 3b564c3..f45e66e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -19,6 +19,7 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
@@ -1011,21 +1012,23 @@
@VisibleForTesting
protected void applyRestrictions() {
+ final boolean zenOn = mZenMode != Global.ZEN_MODE_OFF;
final boolean zenPriorityOnly = mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
final boolean zenSilence = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
final boolean zenAlarmsOnly = mZenMode == Global.ZEN_MODE_ALARMS;
- final boolean allowCalls = mConsolidatedPolicy.allowCalls();
+ final boolean allowCalls = mConsolidatedPolicy.allowCalls()
+ && mConsolidatedPolicy.allowCallsFrom() == PRIORITY_SENDERS_ANY;
final boolean allowRepeatCallers = mConsolidatedPolicy.allowRepeatCallers();
final boolean allowSystem = mConsolidatedPolicy.allowSystem();
final boolean allowMedia = mConsolidatedPolicy.allowMedia();
final boolean allowAlarms = mConsolidatedPolicy.allowAlarms();
// notification restrictions
- final boolean muteNotifications =
- (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
+ final boolean muteNotifications = zenOn
+ || (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
// call restrictions
final boolean muteCalls = zenAlarmsOnly
- || (zenPriorityOnly && !allowCalls && !allowRepeatCallers)
+ || (zenPriorityOnly && !(allowCalls || allowRepeatCallers))
|| (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
// alarm restrictions
final boolean muteAlarms = zenPriorityOnly && !allowAlarms;
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
index 8bea119..cadb8e4 100644
--- a/services/core/java/com/android/server/om/OverlayReferenceMapper.java
+++ b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
@@ -18,16 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.parsing.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import com.android.server.SystemConfig;
-import com.android.server.pm.PackageSetting;
import java.util.Collections;
import java.util.HashMap;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a440c62..d12e03d 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,7 +32,6 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -47,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.utils.TimingsTraceAndSlog;
import com.google.android.collect.Lists;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index a1250cb..1205a33 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -26,13 +26,12 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
-import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
import android.os.Binder;
import android.os.Process;
import android.os.Trace;
@@ -50,6 +49,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
import com.android.server.om.OverlayReferenceMapper;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
import java.util.List;
@@ -207,14 +207,14 @@
/** Returns true if the querying package may query for the potential target package */
private static boolean canQueryViaComponents(AndroidPackage querying,
AndroidPackage potentialTarget) {
- if (querying.getQueriesIntents() != null) {
+ if (!querying.getQueriesIntents().isEmpty()) {
for (Intent intent : querying.getQueriesIntents()) {
if (matchesIntentFilters(intent, potentialTarget)) {
return true;
}
}
}
- if (querying.getQueriesProviders() != null
+ if (!querying.getQueriesProviders().isEmpty()
&& matchesProviders(querying.getQueriesProviders(), potentialTarget)) {
return true;
}
@@ -223,7 +223,7 @@
private static boolean canQueryViaPackage(AndroidPackage querying,
AndroidPackage potentialTarget) {
- return querying.getQueriesPackages() != null
+ return !querying.getQueriesPackages().isEmpty()
&& querying.getQueriesPackages().contains(potentialTarget.getPackageName());
}
@@ -261,7 +261,7 @@
private static boolean matchesIntentFilters(Intent intent, AndroidPackage potentialTarget) {
for (int s = ArrayUtils.size(potentialTarget.getServices()) - 1; s >= 0; s--) {
ParsedService service = potentialTarget.getServices().get(s);
- if (!service.exported) {
+ if (!service.isExported()) {
continue;
}
if (matchesAnyFilter(intent, service)) {
@@ -270,7 +270,7 @@
}
for (int a = ArrayUtils.size(potentialTarget.getActivities()) - 1; a >= 0; a--) {
ParsedActivity activity = potentialTarget.getActivities().get(a);
- if (!activity.exported) {
+ if (!activity.isExported()) {
continue;
}
if (matchesAnyFilter(intent, activity)) {
@@ -279,7 +279,7 @@
}
for (int r = ArrayUtils.size(potentialTarget.getReceivers()) - 1; r >= 0; r--) {
ParsedActivity receiver = potentialTarget.getReceivers().get(r);
- if (!receiver.exported) {
+ if (!receiver.isExported()) {
continue;
}
if (matchesAnyFilter(intent, receiver)) {
@@ -289,9 +289,8 @@
return false;
}
- private static boolean matchesAnyFilter(
- Intent intent, ParsedComponent<? extends ParsedIntentInfo> component) {
- List<? extends ParsedIntentInfo> intents = component.intents;
+ private static boolean matchesAnyFilter(Intent intent, ParsedComponent component) {
+ List<ParsedIntentInfo> intents = component.getIntents();
for (int i = ArrayUtils.size(intents) - 1; i >= 0; i--) {
IntentFilter intentFilter = intents.get(i);
if (intentFilter.match(intent.getAction(), intent.getType(), intent.getScheme(),
@@ -673,8 +672,7 @@
String targetName) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingPkgInstruments");
- final List<ComponentParseUtils.ParsedInstrumentation> inst =
- callingPkgSetting.pkg.getInstrumentations();
+ final List<ParsedInstrumentation> inst = callingPkgSetting.pkg.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
if (Objects.equals(inst.get(i).getTargetPackage(), targetName)) {
if (DEBUG_LOGGING) {
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 0dacadc..85810e3 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -22,12 +22,12 @@
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.InstantAppResolveInfo;
import android.content.pm.PackageManager;
@@ -37,15 +37,12 @@
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProviderIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
-import android.content.pm.parsing.ComponentParseUtils.ParsedServiceIntentInfo;
-import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -58,6 +55,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -191,8 +190,11 @@
* of these during boot as we need to inspect at all of the intent filters on the
* /system partition in order to know which component is the setup wizard. This can
* only ever be non-empty if {@link #mDeferProtectedFilters} is {@code true}.
+ *
+ * This is a pair of component package name to actual filter, because we don't store the
+ * name inside the filter. It's technically independent of the component it's contained in.
*/
- private List<ParsedActivityIntentInfo> mProtectedFilters;
+ private List<Pair<ParsedMainComponent, ParsedIntentInfo>> mProtectedFilters;
ComponentResolver(UserManagerService userManager,
PackageManagerInternal packageManagerInternal,
@@ -299,7 +301,7 @@
continue;
}
final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
- pkg, p, flags, ps.readUserState(userId), userId);
+ pkg, p, flags, ps.readUserState(userId), userId, ps);
if (info == null) {
continue;
}
@@ -329,7 +331,7 @@
return null;
}
return PackageInfoUtils.generateProviderInfo(pkg, p, flags,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
}
}
@@ -354,12 +356,12 @@
continue;
}
- if (safeMode && (pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if (safeMode && !pkg.isSystem()) {
continue;
}
final ProviderInfo info =
PackageInfoUtils.generateProviderInfo(pkg, p, 0,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
if (info == null) {
continue;
}
@@ -415,7 +417,7 @@
/** Add all components defined in the given package to the internal structures. */
void addAllComponents(AndroidPackage pkg, boolean chatty) {
- final ArrayList<ParsedActivityIntentInfo> newIntents = new ArrayList<>();
+ final ArrayList<Pair<ParsedActivity, ParsedIntentInfo>> newIntents = new ArrayList<>();
synchronized (mLock) {
addActivitiesLocked(pkg, newIntents, chatty);
addReceiversLocked(pkg, chatty);
@@ -428,14 +430,14 @@
PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
for (int i = newIntents.size() - 1; i >= 0; --i) {
- final ParsedActivityIntentInfo intentInfo = newIntents.get(i);
+ final Pair<ParsedActivity, ParsedIntentInfo> pair = newIntents.get(i);
final PackageSetting disabledPkgSetting = (PackageSetting) sPackageManagerInternal
- .getDisabledSystemPackage(intentInfo.getPackageName());
+ .getDisabledSystemPackage(pair.first.getPackageName());
final AndroidPackage disabledPkg =
disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
final List<ParsedActivity> systemActivities =
disabledPkg != null ? disabledPkg.getActivities() : null;
- adjustPriority(systemActivities, intentInfo, setupWizardPackage);
+ adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
}
}
@@ -459,7 +461,8 @@
if (mProtectedFilters == null || mProtectedFilters.size() == 0) {
return;
}
- final List<ParsedActivityIntentInfo> protectedFilters = mProtectedFilters;
+ final List<Pair<ParsedMainComponent, ParsedIntentInfo>> protectedFilters =
+ mProtectedFilters;
mProtectedFilters = null;
// expect single setupwizard package
@@ -472,13 +475,17 @@
+ " All protected intents capped to priority 0");
}
for (int i = protectedFilters.size() - 1; i >= 0; --i) {
- final ParsedActivityIntentInfo filter = protectedFilters.get(i);
- if (filter.getPackageName().equals(setupWizardPackage)) {
+ final Pair<ParsedMainComponent, ParsedIntentInfo> pair = protectedFilters.get(i);
+ ParsedMainComponent component = pair.first;
+ ParsedIntentInfo filter = pair.second;
+ String packageName = component.getPackageName();
+ String className = component.getClassName();
+ if (packageName.equals(setupWizardPackage)) {
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found setup wizard;"
+ " allow priority " + filter.getPriority() + ";"
- + " package: " + filter.getPackageName()
- + " activity: " + filter.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " priority: " + filter.getPriority());
}
// skip setup wizard; allow it to keep the high priority filter
@@ -486,8 +493,8 @@
}
if (DEBUG_FILTERS) {
Slog.i(TAG, "Protected action; cap priority to 0;"
- + " package: " + filter.getPackageName()
- + " activity: " + filter.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + filter.getPriority());
}
filter.setPriority(0);
@@ -540,7 +547,7 @@
printedSomething = true;
}
pw.print(" ");
- ComponentName.printShortString(pw, p.getPackageName(), p.className);
+ ComponentName.printShortString(pw, p.getPackageName(), p.getName());
pw.println(":");
pw.print(" ");
pw.println(p.toString());
@@ -575,24 +582,11 @@
if (dumpState.onTitlePrinted()) pw.println();
pw.println("Service permissions:");
- final Iterator<ParsedServiceIntentInfo> filterIterator = mServices.filterIterator();
+ final Iterator<Pair<ParsedService, ParsedIntentInfo>> filterIterator =
+ mServices.filterIterator();
while (filterIterator.hasNext()) {
- final ParsedServiceIntentInfo info = filterIterator.next();
-
- ParsedService service = null;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(info.getPackageName());
- if (pkg != null && pkg.getServices() != null) {
- for (ParsedService parsedService : pkg.getServices()) {
- if (Objects.equals(parsedService.className, info.getClassName())) {
- service = parsedService;
- }
- }
- }
-
- if (service == null) {
- continue;
- }
+ final Pair<ParsedService, ParsedIntentInfo> pair = filterIterator.next();
+ ParsedService service = pair.first;
final String permission = service.getPermission();
if (permission != null) {
@@ -606,7 +600,7 @@
@GuardedBy("mLock")
private void addActivitiesLocked(AndroidPackage pkg,
- List<ParsedActivityIntentInfo> newIntents, boolean chatty) {
+ List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents, boolean chatty) {
final int activitiesSize = ArrayUtils.size(pkg.getActivities());
StringBuilder r = null;
for (int i = 0; i < activitiesSize; i++) {
@@ -671,7 +665,7 @@
final String packageName =
component != null ? component.getPackageName() : "?";
Slog.w(TAG, "Skipping provider name " + names[j]
- + " (in package " + pkg.getAppInfoPackageName() + ")"
+ + " (in package " + pkg.getPackageName() + ")"
+ ": name already used by " + packageName);
}
}
@@ -736,8 +730,8 @@
* <em>WARNING</em> for performance reasons, the passed in intentList WILL BE
* MODIFIED. Do not pass in a list that should not be changed.
*/
- private static <T> void getIntentListSubset(List<ParsedActivityIntentInfo> intentList,
- Function<ParsedActivityIntentInfo, Iterator<T>> generator, Iterator<T> searchIterator) {
+ private static <T> void getIntentListSubset(List<ParsedIntentInfo> intentList,
+ Function<ParsedIntentInfo, Iterator<T>> generator, Iterator<T> searchIterator) {
// loop through the set of actions; every one must be found in the intent filter
while (searchIterator.hasNext()) {
// we must have at least one filter in the list to consider a match
@@ -748,9 +742,9 @@
final T searchAction = searchIterator.next();
// loop through the set of intent filters
- final Iterator<ParsedActivityIntentInfo> intentIter = intentList.iterator();
+ final Iterator<ParsedIntentInfo> intentIter = intentList.iterator();
while (intentIter.hasNext()) {
- final ParsedActivityIntentInfo intentInfo = intentIter.next();
+ final ParsedIntentInfo intentInfo = intentIter.next();
boolean selectionFound = false;
// loop through the intent filter's selection criteria; at least one
@@ -773,7 +767,7 @@
}
}
- private static boolean isProtectedAction(ParsedActivityIntentInfo filter) {
+ private static boolean isProtectedAction(ParsedIntentInfo filter) {
final Iterator<String> actionsIter = filter.actionsIterator();
while (actionsIter != null && actionsIter.hasNext()) {
final String filterAction = actionsIter.next();
@@ -793,14 +787,14 @@
if (sysActivity.getName().equals(activityInfo.getName())) {
return sysActivity;
}
- if (sysActivity.getName().equals(activityInfo.targetActivity)) {
+ if (sysActivity.getName().equals(activityInfo.getTargetActivity())) {
return sysActivity;
}
- if (sysActivity.targetActivity != null) {
- if (sysActivity.targetActivity.equals(activityInfo.getName())) {
+ if (sysActivity.getTargetActivity() != null) {
+ if (sysActivity.getTargetActivity().equals(activityInfo.getName())) {
return sysActivity;
}
- if (sysActivity.targetActivity.equals(activityInfo.targetActivity)) {
+ if (sysActivity.getTargetActivity().equals(activityInfo.getTargetActivity())) {
return sysActivity;
}
}
@@ -821,23 +815,24 @@
* <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is
* allowed to obtain any priority on any action.
*/
- private void adjustPriority(List<ParsedActivity> systemActivities,
- ParsedActivityIntentInfo intent, String setupWizardPackage) {
+ private void adjustPriority(List<ParsedActivity> systemActivities, ParsedActivity activity,
+ ParsedIntentInfo intent, String setupWizardPackage) {
// nothing to do; priority is fine as-is
if (intent.getPriority() <= 0) {
return;
}
- AndroidPackage pkg = sPackageManagerInternal.getPackage(intent.getPackageName());
+ String packageName = activity.getPackageName();
+ AndroidPackage pkg = sPackageManagerInternal.getPackage(packageName);
- final boolean privilegedApp =
- ((pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);
+ final boolean privilegedApp = pkg.isPrivileged();
+ String className = activity.getClassName();
if (!privilegedApp) {
// non-privileged applications can never define a priority >0
if (DEBUG_FILTERS) {
Slog.i(TAG, "Non-privileged app; cap priority to 0;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -858,11 +853,11 @@
if (mProtectedFilters == null) {
mProtectedFilters = new ArrayList<>();
}
- mProtectedFilters.add(intent);
+ mProtectedFilters.add(Pair.create(activity, intent));
if (DEBUG_FILTERS) {
Slog.i(TAG, "Protected action; save for later;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
return;
@@ -871,12 +866,12 @@
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
}
- if (intent.getPackageName().equals(setupWizardPackage)) {
+ if (packageName.equals(setupWizardPackage)) {
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found setup wizard;"
+ " allow priority " + intent.getPriority() + ";"
- + " package: " + intent.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " priority: " + intent.getPriority());
}
// setup wizard gets whatever it wants
@@ -884,8 +879,8 @@
}
if (DEBUG_FILTERS) {
Slog.i(TAG, "Protected action; cap priority to 0;"
- + " package: " + intent.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -898,27 +893,13 @@
// privileged app unbundled update ... try to find the same activity
- ParsedActivity foundActivity = null;
- ParsedActivity activity = null;
-
- if (pkg.getActivities() != null) {
- for (ParsedActivity parsedProvider : pkg.getActivities()) {
- if (Objects.equals(parsedProvider.className, intent.getClassName())) {
- activity = parsedProvider;
- }
- }
- }
-
- if (activity != null) {
- foundActivity = findMatchingActivity(systemActivities, activity);
- }
-
+ ParsedActivity foundActivity = findMatchingActivity(systemActivities, activity);
if (foundActivity == null) {
// this is a new activity; it cannot obtain >0 priority
if (DEBUG_FILTERS) {
Slog.i(TAG, "New activity; cap priority to 0;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -928,8 +909,8 @@
// found activity, now check for filter equivalence
// a shallow copy is enough; we modify the list, not its contents
- final List<ParsedActivityIntentInfo> intentListCopy =
- new ArrayList<>(foundActivity.intents);
+ final List<ParsedIntentInfo> intentListCopy =
+ new ArrayList<>(foundActivity.getIntents());
// find matching action subsets
final Iterator<String> actionsIterator = intent.actionsIterator();
@@ -939,8 +920,8 @@
// no more intents to match; we're not equivalent
if (DEBUG_FILTERS) {
Slog.i(TAG, "Mismatched action; cap priority to 0;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -957,8 +938,8 @@
// no more intents to match; we're not equivalent
if (DEBUG_FILTERS) {
Slog.i(TAG, "Mismatched category; cap priority to 0;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -974,8 +955,8 @@
// no more intents to match; we're not equivalent
if (DEBUG_FILTERS) {
Slog.i(TAG, "Mismatched scheme; cap priority to 0;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -993,8 +974,8 @@
// no more intents to match; we're not equivalent
if (DEBUG_FILTERS) {
Slog.i(TAG, "Mismatched authority; cap priority to 0;"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(0);
@@ -1011,8 +992,8 @@
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found matching filter(s);"
+ " cap priority to " + cappedPriority + ";"
- + " package: " + pkg.getPackageName()
- + " activity: " + intent.getClassName()
+ + " package: " + packageName
+ + " activity: " + className
+ " origPrio: " + intent.getPriority());
}
intent.setPriority(cappedPriority);
@@ -1146,31 +1127,34 @@
}
}
- private abstract static class MimeGroupsAwareIntentResolver<F extends ParsedIntentInfo, R>
+ private abstract static class MimeGroupsAwareIntentResolver<F extends Pair<?
+ extends ParsedComponent, ParsedIntentInfo>, R>
extends IntentResolver<F, R> {
private ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
private boolean mIsUpdatingMimeGroup = false;
@Override
public void addFilter(F f) {
+ IntentFilter intentFilter = getIntentFilter(f);
applyMimeGroups(f);
super.addFilter(f);
if (!mIsUpdatingMimeGroup) {
- register_intent_filter(f, f.mimeGroupsIterator(), mMimeGroupToFilter,
+ register_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter,
" MimeGroup: ");
}
}
@Override
protected void removeFilterInternal(F f) {
+ IntentFilter intentFilter = getIntentFilter(f);
if (!mIsUpdatingMimeGroup) {
- unregister_intent_filter(f, f.mimeGroupsIterator(), mMimeGroupToFilter,
+ unregister_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter,
" MimeGroup: ");
}
super.removeFilterInternal(f);
- f.clearDynamicDataTypes();
+ intentFilter.clearDynamicDataTypes();
}
/**
@@ -1197,10 +1181,11 @@
return hasChanges;
}
- private boolean updateFilter(F filter) {
+ private boolean updateFilter(F f) {
+ IntentFilter filter = getIntentFilter(f);
List<String> oldTypes = filter.dataTypes();
- removeFilter(filter);
- addFilter(filter);
+ removeFilter(f);
+ addFilter(f);
List<String> newTypes = filter.dataTypes();
return !equalLists(oldTypes, newTypes);
}
@@ -1221,12 +1206,12 @@
return first.equals(second);
}
- private void applyMimeGroups(F filter) {
- String packageName = filter.getPackageName();
+ private void applyMimeGroups(F f) {
+ IntentFilter filter = getIntentFilter(f);
for (int i = filter.countMimeGroups() - 1; i >= 0; i--) {
- List<String> mimeTypes = sPackageManagerInternal.getMimeGroup(packageName,
- filter.getMimeGroup(i));
+ List<String> mimeTypes = sPackageManagerInternal.getMimeGroup(
+ f.first.getPackageName(), filter.getMimeGroup(i));
for (int typeIndex = mimeTypes.size() - 1; typeIndex >= 0; typeIndex--) {
String mimeType = mimeTypes.get(typeIndex);
@@ -1244,7 +1229,7 @@
}
private static class ActivityIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedActivityIntentInfo, ResolveInfo> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
@@ -1277,15 +1262,18 @@
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int activitiesSize = packageActivities.size();
- ArrayList<ParsedActivityIntentInfo[]> listCut = new ArrayList<>(activitiesSize);
+ ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut =
+ new ArrayList<>(activitiesSize);
- List<ParsedActivityIntentInfo> intentFilters;
+ List<ParsedIntentInfo> intentFilters;
for (int i = 0; i < activitiesSize; ++i) {
- intentFilters = packageActivities.get(i).intents;
- if (intentFilters != null && intentFilters.size() > 0) {
- ParsedActivityIntentInfo[] array =
- new ParsedActivityIntentInfo[intentFilters.size()];
- intentFilters.toArray(array);
+ ParsedActivity activity = packageActivities.get(i);
+ intentFilters = activity.getIntents();
+ if (!intentFilters.isEmpty()) {
+ Pair<ParsedActivity, ParsedIntentInfo>[] array = newArray(intentFilters.size());
+ for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
+ array[arrayIndex] = Pair.create(activity, intentFilters.get(arrayIndex));
+ }
listCut.add(array);
}
}
@@ -1293,22 +1281,17 @@
}
private void addActivity(ParsedActivity a, String type,
- List<ParsedActivityIntentInfo> newIntents) {
+ List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
mActivities.put(a.getComponentName(), a);
if (DEBUG_SHOW_INFO) {
- final CharSequence label = a.nonLocalizedLabel != null
- ? a.nonLocalizedLabel
- : a.getName();
- Log.v(TAG, " " + type + " " + label + ":");
- }
- if (DEBUG_SHOW_INFO) {
+ Log.v(TAG, " " + type + ":");
Log.v(TAG, " Class=" + a.getName());
}
- final int intentsSize = a.intents.size();
+ final int intentsSize = a.getIntents().size();
for (int j = 0; j < intentsSize; j++) {
- ParsedActivityIntentInfo intent = a.intents.get(j);
+ ParsedIntentInfo intent = a.getIntents().get(j);
if (newIntents != null && "activity".equals(type)) {
- newIntents.add(intent);
+ newIntents.add(Pair.create(a, intent));
}
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
@@ -1317,36 +1300,34 @@
if (!intent.debugCheck()) {
Log.w(TAG, "==> For Activity " + a.getName());
}
- addFilter(intent);
+ addFilter(Pair.create(a, intent));
}
}
private void removeActivity(ParsedActivity a, String type) {
mActivities.remove(a.getComponentName());
if (DEBUG_SHOW_INFO) {
- Log.v(TAG, " " + type + " "
- + (a.nonLocalizedLabel != null ? a.nonLocalizedLabel
- : a.getName()) + ":");
+ Log.v(TAG, " " + type + ":");
Log.v(TAG, " Class=" + a.getName());
}
- final int intentsSize = a.intents.size();
+ final int intentsSize = a.getIntents().size();
for (int j = 0; j < intentsSize; j++) {
- ParsedActivityIntentInfo intent = a.intents.get(j);
+ ParsedIntentInfo intent = a.getIntents().get(j);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
- removeFilter(intent);
+ removeFilter(Pair.create(a, intent));
}
}
@Override
- protected boolean allowFilterResult(
- ParsedActivityIntentInfo filter, List<ResolveInfo> dest) {
+ protected boolean allowFilterResult(Pair<ParsedActivity, ParsedIntentInfo> filter,
+ List<ResolveInfo> dest) {
for (int i = dest.size() - 1; i >= 0; --i) {
ActivityInfo destAi = dest.get(i).activityInfo;
- if (Objects.equals(destAi.name, filter.getClassName())
- && Objects.equals(destAi.packageName, filter.getPackageName())) {
+ if (Objects.equals(destAi.name, filter.first.getName())
+ && Objects.equals(destAi.packageName, filter.first.getPackageName())) {
return false;
}
}
@@ -1354,39 +1335,23 @@
}
@Override
- protected ParsedActivityIntentInfo[] newArray(int size) {
- return new ParsedActivityIntentInfo[size];
+ protected Pair<ParsedActivity, ParsedIntentInfo>[] newArray(int size) {
+ //noinspection unchecked
+ return (Pair<ParsedActivity, ParsedIntentInfo>[]) new Pair<?, ?>[size];
}
@Override
- protected boolean isFilterStopped(ParsedActivityIntentInfo filter, int userId) {
- if (!sUserManager.exists(userId)) return true;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg == null) {
- return false;
- }
-
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- filter.getPackageName());
- if (ps == null) {
- return false;
- }
-
- // System apps are never considered stopped for purposes of
- // filtering, because there may be no way for the user to
- // actually re-launch them.
- return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
- && ps.getStopped(userId);
+ protected boolean isFilterStopped(Pair<ParsedActivity, ParsedIntentInfo> filter, int userId) {
+ return ComponentResolver.isFilterStopped(filter, userId);
}
@Override
protected boolean isPackageForFilter(String packageName,
- ParsedActivityIntentInfo info) {
- return packageName.equals(info.getPackageName());
+ Pair<ParsedActivity, ParsedIntentInfo> info) {
+ return packageName.equals(info.first.getPackageName());
}
- private void log(String reason, ParsedActivityIntentInfo info, int match,
+ private void log(String reason, ParsedIntentInfo info, int match,
int userId) {
Slog.w(TAG, reason
+ "; match: "
@@ -1396,8 +1361,11 @@
}
@Override
- protected ResolveInfo newResult(ParsedActivityIntentInfo info,
+ protected ResolveInfo newResult(Pair<ParsedActivity, ParsedIntentInfo> pair,
int match, int userId) {
+ ParsedActivity activity = pair.first;
+ ParsedIntentInfo info = pair.second;
+
if (!sUserManager.exists(userId)) {
if (DEBUG) {
log("User doesn't exist", info, match, userId);
@@ -1405,27 +1373,11 @@
return null;
}
- ParsedActivity activity = null;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(info.getPackageName());
+ AndroidPackage pkg = sPackageManagerInternal.getPackage(activity.getPackageName());
if (pkg == null) {
return null;
}
- // TODO(b/135203078): Consider more efficient ways of doing this.
- List<ParsedActivity> activities = getResolveList(pkg);
- if (activities != null) {
- for (ParsedActivity parsedActivity : activities) {
- if (Objects.equals(parsedActivity.className, info.getClassName())) {
- activity = parsedActivity;
- }
- }
- }
-
- if (activity == null) {
- return null;
- }
-
if (!sPackageManagerInternal.isEnabledAndMatches(activity, mFlags, userId)) {
if (DEBUG) {
log("!PackageManagerInternal.isEnabledAndMatches; mFlags="
@@ -1435,7 +1387,7 @@
return null;
}
PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- info.getPackageName());
+ activity.getPackageName());
if (ps == null) {
if (DEBUG) {
log("info.activity.owner.mExtras == null", info, match, userId);
@@ -1443,8 +1395,8 @@
return null;
}
final PackageUserState userState = ps.readUserState(userId);
- ActivityInfo ai =
- PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags, userState, userId);
+ ActivityInfo ai = PackageInfoUtils.generateActivityInfo(pkg, activity, mFlags,
+ userState, userId, ps);
if (ai == null) {
if (DEBUG) {
log("Failed to create ActivityInfo based on " + activity, info, match,
@@ -1502,19 +1454,20 @@
}
res.handleAllWebDataURI = info.handleAllWebDataURI();
res.priority = info.getPriority();
- res.preferredOrder = pkg.getPreferredOrder();
+ // TODO(b/135203078): This field was unwritten and does nothing
+// res.preferredOrder = pkg.getPreferredOrder();
//System.out.println("Result: " + res.activityInfo.className +
// " = " + res.priority);
res.match = match;
- res.isDefault = info.hasDefault;
- res.labelRes = info.labelRes;
- res.nonLocalizedLabel = info.nonLocalizedLabel;
+ res.isDefault = info.isHasDefault();
+ res.labelRes = info.getLabelRes();
+ res.nonLocalizedLabel = info.getNonLocalizedLabel();
if (sPackageManagerInternal.userNeedsBadging(userId)) {
res.noResourceId = true;
} else {
- res.icon = info.icon;
+ res.icon = info.getIcon();
}
- res.iconResourceId = info.icon;
+ res.iconResourceId = info.getIcon();
res.system = res.activityInfo.applicationInfo.isSystemApp();
res.isInstantAppAvailable = userState.instantApp;
return res;
@@ -1527,43 +1480,44 @@
@Override
protected void dumpFilter(PrintWriter out, String prefix,
- ParsedActivityIntentInfo filter) {
- ParsedActivity activity = null;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg != null && pkg.getActivities() != null) {
- for (ParsedActivity parsedActivity : pkg.getActivities()) {
- if (Objects.equals(parsedActivity.className, filter.getClassName())) {
- activity = parsedActivity;
- }
- }
- }
+ Pair<ParsedActivity, ParsedIntentInfo> pair) {
+ ParsedActivity activity = pair.first;
+ ParsedIntentInfo filter = pair.second;
out.print(prefix);
out.print(Integer.toHexString(System.identityHashCode(activity)));
out.print(' ');
- ComponentName.printShortString(out, filter.getPackageName(), filter.getClassName());
+ ComponentName.printShortString(out, activity.getPackageName(),
+ activity.getClassName());
out.print(" filter ");
out.println(Integer.toHexString(System.identityHashCode(filter)));
}
@Override
- protected Object filterToLabel(ParsedActivityIntentInfo filter) {
+ protected Object filterToLabel(Pair<ParsedActivity, ParsedIntentInfo> filter) {
return filter;
}
protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
- ParsedActivityIntentInfo activity = (ParsedActivityIntentInfo) label;
+ @SuppressWarnings("unchecked") Pair<ParsedActivity, ParsedIntentInfo> pair =
+ (Pair<ParsedActivity, ParsedIntentInfo>) label;
out.print(prefix);
- out.print(Integer.toHexString(System.identityHashCode(activity)));
+ out.print(Integer.toHexString(System.identityHashCode(pair.first)));
out.print(' ');
- ComponentName.printShortString(out, activity.getPackageName(), activity.getClassName());
+ ComponentName.printShortString(out, pair.first.getPackageName(),
+ pair.first.getClassName());
if (count > 1) {
out.print(" ("); out.print(count); out.print(" filters)");
}
out.println();
}
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull Pair<ParsedActivity, ParsedIntentInfo> input) {
+ return input.second;
+ }
+
protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
return pkg.getActivities();
}
@@ -1585,7 +1539,7 @@
}
private static final class ProviderIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedProviderIntentInfo, ResolveInfo> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
@@ -1617,15 +1571,18 @@
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int providersSize = packageProviders.size();
- ArrayList<ParsedProviderIntentInfo[]> listCut = new ArrayList<>(providersSize);
+ ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut =
+ new ArrayList<>(providersSize);
- List<ParsedProviderIntentInfo> intentFilters;
+ List<ParsedIntentInfo> intentFilters;
for (int i = 0; i < providersSize; ++i) {
- intentFilters = packageProviders.get(i).getIntents();
- if (intentFilters != null && intentFilters.size() > 0) {
- ParsedProviderIntentInfo[] array =
- new ParsedProviderIntentInfo[intentFilters.size()];
- intentFilters.toArray(array);
+ ParsedProvider provider = packageProviders.get(i);
+ intentFilters = provider.getIntents();
+ if (!intentFilters.isEmpty()) {
+ Pair<ParsedProvider, ParsedIntentInfo>[] array = newArray(intentFilters.size());
+ for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
+ array[arrayIndex] = Pair.create(provider, intentFilters.get(arrayIndex));
+ }
listCut.add(array);
}
}
@@ -1640,17 +1597,13 @@
mProviders.put(p.getComponentName(), p);
if (DEBUG_SHOW_INFO) {
- Log.v(TAG, " "
- + (p.nonLocalizedLabel != null
- ? p.nonLocalizedLabel
- : p.getName())
- + ":");
+ Log.v(TAG, " provider:");
Log.v(TAG, " Class=" + p.getName());
}
final int intentsSize = p.getIntents().size();
int j;
for (j = 0; j < intentsSize; j++) {
- ParsedProviderIntentInfo intent = p.getIntents().get(j);
+ ParsedIntentInfo intent = p.getIntents().get(j);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
@@ -1658,37 +1611,35 @@
if (!intent.debugCheck()) {
Log.w(TAG, "==> For Provider " + p.getName());
}
- addFilter(intent);
+ addFilter(Pair.create(p, intent));
}
}
void removeProvider(ParsedProvider p) {
mProviders.remove(p.getComponentName());
if (DEBUG_SHOW_INFO) {
- Log.v(TAG, " " + (p.nonLocalizedLabel != null
- ? p.nonLocalizedLabel
- : p.getName()) + ":");
+ Log.v(TAG, " provider:");
Log.v(TAG, " Class=" + p.getName());
}
final int intentsSize = p.getIntents().size();
int j;
for (j = 0; j < intentsSize; j++) {
- ParsedProviderIntentInfo intent = p.getIntents().get(j);
+ ParsedIntentInfo intent = p.getIntents().get(j);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
- removeFilter(intent);
+ removeFilter(Pair.create(p, intent));
}
}
@Override
- protected boolean allowFilterResult(
- ParsedProviderIntentInfo filter, List<ResolveInfo> dest) {
+ protected boolean allowFilterResult(Pair<ParsedProvider, ParsedIntentInfo> filter,
+ List<ResolveInfo> dest) {
for (int i = dest.size() - 1; i >= 0; i--) {
ProviderInfo destPi = dest.get(i).providerInfo;
- if (Objects.equals(destPi.name, filter.getClassName())
- && Objects.equals(destPi.packageName, filter.getPackageName())) {
+ if (Objects.equals(destPi.name, filter.first.getClassName())
+ && Objects.equals(destPi.packageName, filter.first.getPackageName())) {
return false;
}
}
@@ -1696,59 +1647,35 @@
}
@Override
- protected ParsedProviderIntentInfo[] newArray(int size) {
- return new ParsedProviderIntentInfo[size];
+ protected Pair<ParsedProvider, ParsedIntentInfo>[] newArray(int size) {
+ //noinspection unchecked
+ return (Pair<ParsedProvider, ParsedIntentInfo>[]) new Pair<?, ?>[size];
}
@Override
- protected boolean isFilterStopped(ParsedProviderIntentInfo filter, int userId) {
- if (!sUserManager.exists(userId)) {
- return true;
- }
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg == null) {
- return false;
- }
-
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- filter.getPackageName());
- if (ps == null) {
- return false;
- }
-
- // System apps are never considered stopped for purposes of
- // filtering, because there may be no way for the user to
- // actually re-launch them.
- return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
- && ps.getStopped(userId);
+ protected boolean isFilterStopped(Pair<ParsedProvider, ParsedIntentInfo> filter,
+ int userId) {
+ return ComponentResolver.isFilterStopped(filter, userId);
}
@Override
protected boolean isPackageForFilter(String packageName,
- ParsedProviderIntentInfo info) {
- return packageName.equals(info.getPackageName());
+ Pair<ParsedProvider, ParsedIntentInfo> info) {
+ return packageName.equals(info.first.getPackageName());
}
@Override
- protected ResolveInfo newResult(ParsedProviderIntentInfo filter,
+ protected ResolveInfo newResult(Pair<ParsedProvider, ParsedIntentInfo> pair,
int match, int userId) {
if (!sUserManager.exists(userId)) {
return null;
}
- ParsedProvider provider = null;
+ ParsedProvider provider = pair.first;
+ ParsedIntentInfo filter = pair.second;
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg != null && pkg.getProviders() != null) {
- for (ParsedProvider parsedProvider : pkg.getProviders()) {
- if (Objects.equals(parsedProvider.className, filter.getClassName())) {
- provider = parsedProvider;
- }
- }
- }
-
- if (provider == null) {
+ AndroidPackage pkg = sPackageManagerInternal.getPackage(provider.getPackageName());
+ if (pkg == null) {
return null;
}
@@ -1757,7 +1684,7 @@
}
PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- filter.getPackageName());
+ provider.getPackageName());
if (ps == null) {
return null;
}
@@ -1779,8 +1706,8 @@
if (userState.instantApp && ps.isUpdateAvailable()) {
return null;
}
- ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider,
- mFlags, userState, userId);
+ ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
+ userState, userId, ps);
if (pi == null) {
return null;
}
@@ -1790,12 +1717,13 @@
res.filter = filter;
}
res.priority = filter.getPriority();
- res.preferredOrder = pkg.getPreferredOrder();
+ // TODO(b/135203078): This field was unwritten and does nothing
+// res.preferredOrder = pkg.getPreferredOrder();
res.match = match;
- res.isDefault = filter.hasDefault;
- res.labelRes = filter.labelRes;
- res.nonLocalizedLabel = filter.nonLocalizedLabel;
- res.icon = filter.icon;
+ res.isDefault = filter.isHasDefault();
+ res.labelRes = filter.getLabelRes();
+ res.nonLocalizedLabel = filter.getNonLocalizedLabel();
+ res.icon = filter.getIcon();
res.system = res.providerInfo.applicationInfo.isSystemApp();
return res;
}
@@ -1807,37 +1735,31 @@
@Override
protected void dumpFilter(PrintWriter out, String prefix,
- ParsedProviderIntentInfo filter) {
- ParsedProvider provider = null;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg != null && pkg.getProviders() != null) {
- for (ParsedProvider parsedProvider : pkg.getProviders()) {
- if (Objects.equals(parsedProvider.className, filter.getClassName())) {
- provider = parsedProvider;
- }
- }
- }
+ Pair<ParsedProvider, ParsedIntentInfo> pair) {
+ ParsedProvider provider = pair.first;
+ ParsedIntentInfo filter = pair.second;
out.print(prefix);
out.print(Integer.toHexString(System.identityHashCode(provider)));
out.print(' ');
- ComponentName.printShortString(out, filter.getPackageName(), filter.getClassName());
+ ComponentName.printShortString(out, provider.getPackageName(), provider.getClassName());
out.print(" filter ");
out.println(Integer.toHexString(System.identityHashCode(filter)));
}
@Override
- protected Object filterToLabel(ParsedProviderIntentInfo filter) {
+ protected Object filterToLabel(Pair<ParsedProvider, ParsedIntentInfo> filter) {
return filter;
}
protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
- final ParsedProviderIntentInfo provider = (ParsedProviderIntentInfo) label;
+ @SuppressWarnings("unchecked") final Pair<ParsedProvider, ParsedIntentInfo> pair =
+ (Pair<ParsedProvider, ParsedIntentInfo>) label;
out.print(prefix);
- out.print(Integer.toHexString(System.identityHashCode(provider)));
+ out.print(Integer.toHexString(System.identityHashCode(pair.first)));
out.print(' ');
- ComponentName.printShortString(out, provider.getPackageName(), provider.getClassName());
+ ComponentName.printShortString(out, pair.first.getPackageName(),
+ pair.first.getClassName());
if (count > 1) {
out.print(" (");
out.print(count);
@@ -1846,12 +1768,18 @@
out.println();
}
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull Pair<ParsedProvider, ParsedIntentInfo> input) {
+ return input.second;
+ }
+
private final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>();
private int mFlags;
}
private static final class ServiceIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedServiceIntentInfo, ResolveInfo> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
@@ -1877,15 +1805,18 @@
mFlags = flags;
final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0;
final int servicesSize = packageServices.size();
- ArrayList<ParsedServiceIntentInfo[]> listCut = new ArrayList<>(servicesSize);
+ ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut =
+ new ArrayList<>(servicesSize);
- List<ParsedServiceIntentInfo> intentFilters;
+ List<ParsedIntentInfo> intentFilters;
for (int i = 0; i < servicesSize; ++i) {
- intentFilters = packageServices.get(i).intents;
- if (intentFilters != null && intentFilters.size() > 0) {
- ParsedServiceIntentInfo[] array =
- new ParsedServiceIntentInfo[intentFilters.size()];
- intentFilters.toArray(array);
+ ParsedService service = packageServices.get(i);
+ intentFilters = service.getIntents();
+ if (intentFilters.size() > 0) {
+ Pair<ParsedService, ParsedIntentInfo>[] array = newArray(intentFilters.size());
+ for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) {
+ array[arrayIndex] = Pair.create(service, intentFilters.get(arrayIndex));
+ }
listCut.add(array);
}
}
@@ -1895,15 +1826,13 @@
void addService(ParsedService s) {
mServices.put(s.getComponentName(), s);
if (DEBUG_SHOW_INFO) {
- Log.v(TAG, " "
- + (s.nonLocalizedLabel != null
- ? s.nonLocalizedLabel : s.getName()) + ":");
+ Log.v(TAG, " service:");
Log.v(TAG, " Class=" + s.getName());
}
- final int intentsSize = s.intents.size();
+ final int intentsSize = s.getIntents().size();
int j;
for (j = 0; j < intentsSize; j++) {
- ParsedServiceIntentInfo intent = s.intents.get(j);
+ ParsedIntentInfo intent = s.getIntents().get(j);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
@@ -1911,36 +1840,35 @@
if (!intent.debugCheck()) {
Log.w(TAG, "==> For Service " + s.getName());
}
- addFilter(intent);
+ addFilter(Pair.create(s, intent));
}
}
void removeService(ParsedService s) {
mServices.remove(s.getComponentName());
if (DEBUG_SHOW_INFO) {
- Log.v(TAG, " " + (s.nonLocalizedLabel != null
- ? s.nonLocalizedLabel : s.getName()) + ":");
+ Log.v(TAG, " service:");
Log.v(TAG, " Class=" + s.getName());
}
- final int intentsSize = s.intents.size();
+ final int intentsSize = s.getIntents().size();
int j;
for (j = 0; j < intentsSize; j++) {
- ParsedServiceIntentInfo intent = s.intents.get(j);
+ ParsedIntentInfo intent = s.getIntents().get(j);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
}
- removeFilter(intent);
+ removeFilter(Pair.create(s, intent));
}
}
@Override
- protected boolean allowFilterResult(
- ParsedServiceIntentInfo filter, List<ResolveInfo> dest) {
+ protected boolean allowFilterResult(Pair<ParsedService, ParsedIntentInfo> filter,
+ List<ResolveInfo> dest) {
for (int i = dest.size() - 1; i >= 0; --i) {
ServiceInfo destAi = dest.get(i).serviceInfo;
- if (Objects.equals(destAi.name, filter.getClassName())
- && Objects.equals(destAi.packageName, filter.getPackageName())) {
+ if (Objects.equals(destAi.name, filter.first.getClassName())
+ && Objects.equals(destAi.packageName, filter.first.getPackageName())) {
return false;
}
}
@@ -1948,55 +1876,32 @@
}
@Override
- protected ParsedServiceIntentInfo[] newArray(int size) {
- return new ParsedServiceIntentInfo[size];
+ protected Pair<ParsedService, ParsedIntentInfo>[] newArray(int size) {
+ //noinspection unchecked
+ return (Pair<ParsedService, ParsedIntentInfo>[]) new Pair<?, ?>[size];
}
@Override
- protected boolean isFilterStopped(ParsedServiceIntentInfo filter, int userId) {
- if (!sUserManager.exists(userId)) return true;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg == null) {
- return false;
- }
-
- PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- filter.getPackageName());
- if (ps == null) {
- return false;
- }
-
- // System apps are never considered stopped for purposes of
- // filtering, because there may be no way for the user to
- // actually re-launch them.
- return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0
- && ps.getStopped(userId);
+ protected boolean isFilterStopped(Pair<ParsedService, ParsedIntentInfo> filter, int userId) {
+ return ComponentResolver.isFilterStopped(filter, userId);
}
@Override
protected boolean isPackageForFilter(String packageName,
- ParsedServiceIntentInfo info) {
- return packageName.equals(info.getPackageName());
+ Pair<ParsedService, ParsedIntentInfo> info) {
+ return packageName.equals(info.first.getPackageName());
}
@Override
- protected ResolveInfo newResult(ParsedServiceIntentInfo filter,
- int match, int userId) {
+ protected ResolveInfo newResult(Pair<ParsedService, ParsedIntentInfo> pair, int match,
+ int userId) {
if (!sUserManager.exists(userId)) return null;
- ParsedService service = null;
+ ParsedService service = pair.first;
+ ParsedIntentInfo filter = pair.second;
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg != null && pkg.getServices() != null) {
- for (ParsedService parsedService : pkg.getServices()) {
- if (Objects.equals(parsedService.className, filter.getClassName())) {
- service = parsedService;
- }
- }
- }
-
- if (service == null) {
+ AndroidPackage pkg = sPackageManagerInternal.getPackage(service.getPackageName());
+ if (pkg == null) {
return null;
}
@@ -2005,13 +1910,13 @@
}
PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
- filter.getPackageName());
+ service.getPackageName());
if (ps == null) {
return null;
}
final PackageUserState userState = ps.readUserState(userId);
ServiceInfo si = PackageInfoUtils.generateServiceInfo(pkg, service, mFlags,
- userState, userId);
+ userState, userId, ps);
if (si == null) {
return null;
}
@@ -2038,12 +1943,13 @@
res.filter = filter;
}
res.priority = filter.getPriority();
- res.preferredOrder = pkg.getPreferredOrder();
+ // TODO(b/135203078): This field was unwritten and does nothing
+// res.preferredOrder = pkg.getPreferredOrder();
res.match = match;
- res.isDefault = filter.hasDefault;
- res.labelRes = filter.labelRes;
- res.nonLocalizedLabel = filter.nonLocalizedLabel;
- res.icon = filter.icon;
+ res.isDefault = filter.isHasDefault();
+ res.labelRes = filter.getLabelRes();
+ res.nonLocalizedLabel = filter.getNonLocalizedLabel();
+ res.icon = filter.getIcon();
res.system = res.serviceInfo.applicationInfo.isSystemApp();
return res;
}
@@ -2055,25 +1961,17 @@
@Override
protected void dumpFilter(PrintWriter out, String prefix,
- ParsedServiceIntentInfo filter) {
- ParsedService service = null;
-
- AndroidPackage pkg = sPackageManagerInternal.getPackage(filter.getPackageName());
- if (pkg != null && pkg.getServices() != null) {
- for (ParsedService parsedService : pkg.getServices()) {
- if (Objects.equals(parsedService.className, filter.getClassName())) {
- service = parsedService;
- }
- }
- }
+ Pair<ParsedService, ParsedIntentInfo> pair) {
+ ParsedService service = pair.first;
+ ParsedIntentInfo filter = pair.second;
out.print(prefix);
out.print(Integer.toHexString(System.identityHashCode(service)));
out.print(' ');
- ComponentName.printShortString(out, filter.getPackageName(), filter.getClassName());
+ ComponentName.printShortString(out, service.getPackageName(), service.getClassName());
out.print(" filter ");
out.print(Integer.toHexString(System.identityHashCode(filter)));
- if (service != null && service.getPermission() != null) {
+ if (service.getPermission() != null) {
out.print(" permission "); out.println(service.getPermission());
} else {
out.println();
@@ -2081,22 +1979,30 @@
}
@Override
- protected Object filterToLabel(ParsedServiceIntentInfo filter) {
+ protected Object filterToLabel(Pair<ParsedService, ParsedIntentInfo> filter) {
return filter;
}
protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
- final ParsedServiceIntentInfo service = (ParsedServiceIntentInfo) label;
+ @SuppressWarnings("unchecked") final Pair<ParsedService, ParsedIntentInfo> pair =
+ (Pair<ParsedService, ParsedIntentInfo>) label;
out.print(prefix);
- out.print(Integer.toHexString(System.identityHashCode(service)));
+ out.print(Integer.toHexString(System.identityHashCode(pair.first)));
out.print(' ');
- ComponentName.printShortString(out, service.getPackageName(), service.getClassName());
+ ComponentName.printShortString(out, pair.first.getPackageName(),
+ pair.first.getClassName());
if (count > 1) {
out.print(" ("); out.print(count); out.print(" filters)");
}
out.println();
}
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull Pair<ParsedService, ParsedIntentInfo> input) {
+ return input.second;
+ }
+
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>();
private int mFlags;
@@ -2183,11 +2089,40 @@
i--;
}
}
+
+ @Override
+ protected IntentFilter getIntentFilter(
+ @NonNull AuxiliaryResolveInfo.AuxiliaryFilter input) {
+ return input;
+ }
+ }
+
+ private static boolean isFilterStopped(Pair<? extends ParsedComponent, ParsedIntentInfo> pair,
+ int userId) {
+ if (!sUserManager.exists(userId)) {
+ return true;
+ }
+
+ AndroidPackage pkg = sPackageManagerInternal.getPackage(pair.first.getPackageName());
+ if (pkg == null) {
+ return false;
+ }
+
+ PackageSetting ps = (PackageSetting) sPackageManagerInternal.getPackageSetting(
+ pair.first.getPackageName());
+ if (ps == null) {
+ return false;
+ }
+
+ // System apps are never considered stopped for purposes of
+ // filtering, because there may be no way for the user to
+ // actually re-launch them.
+ return !ps.isSystem() && ps.getStopped(userId);
}
/** Generic to create an {@link Iterator} for a data type */
static class IterGenerator<E> {
- public Iterator<E> generate(ParsedActivityIntentInfo info) {
+ public Iterator<E> generate(ParsedIntentInfo info) {
return null;
}
}
@@ -2195,7 +2130,7 @@
/** Create an {@link Iterator} for intent actions */
static class ActionIterGenerator extends IterGenerator<String> {
@Override
- public Iterator<String> generate(ParsedActivityIntentInfo info) {
+ public Iterator<String> generate(ParsedIntentInfo info) {
return info.actionsIterator();
}
}
@@ -2203,7 +2138,7 @@
/** Create an {@link Iterator} for intent categories */
static class CategoriesIterGenerator extends IterGenerator<String> {
@Override
- public Iterator<String> generate(ParsedActivityIntentInfo info) {
+ public Iterator<String> generate(ParsedIntentInfo info) {
return info.categoriesIterator();
}
}
@@ -2211,7 +2146,7 @@
/** Create an {@link Iterator} for intent schemes */
static class SchemesIterGenerator extends IterGenerator<String> {
@Override
- public Iterator<String> generate(ParsedActivityIntentInfo info) {
+ public Iterator<String> generate(ParsedIntentInfo info) {
return info.schemesIterator();
}
}
@@ -2219,7 +2154,7 @@
/** Create an {@link Iterator} for intent authorities */
static class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> {
@Override
- public Iterator<IntentFilter.AuthorityEntry> generate(ParsedActivityIntentInfo info) {
+ public Iterator<IntentFilter.AuthorityEntry> generate(ParsedIntentInfo info) {
return info.authoritiesIterator();
}
}
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
index 0e0096d..8e6b89a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
@@ -18,6 +18,9 @@
package com.android.server.pm;
+import android.annotation.NonNull;
+import android.content.IntentFilter;
+
import com.android.server.IntentResolver;
import java.util.List;
@@ -40,4 +43,9 @@
protected void sortResults(List<CrossProfileIntentFilter> results) {
//We don't sort the results
}
+
+ @Override
+ protected IntentFilter getIntentFilter(@NonNull CrossProfileIntentFilter input) {
+ return input;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 8ad3e9d..f37af3a 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -234,11 +234,11 @@
}
public void moveCompleteApp(String fromUuid, String toUuid, String packageName,
- String dataAppName, int appId, String seInfo, int targetSdkVersion,
+ int appId, String seInfo, int targetSdkVersion,
String fromCodePath) throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo,
+ mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, appId, seInfo,
targetSdkVersion, fromCodePath);
} catch (Exception e) {
throw InstallerException.from(e);
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index bcfe577..cf85b0f 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -24,8 +24,6 @@
import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageInfoUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -52,6 +50,8 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.io.IoUtils;
import libcore.util.HexEncoding;
@@ -694,12 +694,13 @@
final int packageCount = mService.mPackages.size();
for (int i = 0; i < packageCount; i++) {
final AndroidPackage pkg = mService.mPackages.valueAt(i);
- if (now - pkg.getLatestPackageUseTimeInMills() < maxInstalledCacheDuration) {
+ final PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
+ if (ps == null) {
continue;
}
- final PackageSetting ps = mService.getPackageSetting(pkg.getPackageName());
- if (ps == null) {
+ if (now - ps.getPkgState().getLatestPackageUseTimeInMills()
+ < maxInstalledCacheDuration) {
continue;
}
@@ -733,30 +734,28 @@
} else if (rhsPkg == null) {
return 1;
} else {
- if (lhsPkg.getLatestPackageUseTimeInMills() >
- rhsPkg.getLatestPackageUseTimeInMills()) {
+ final PackageSetting lhsPs = mService.getPackageSetting(
+ lhsPkg.getPackageName());
+ if (lhsPs == null) {
+ return 0;
+ }
+
+ final PackageSetting rhsPs = mService.getPackageSetting(
+ rhsPkg.getPackageName());
+ if (rhsPs == null) {
+ return 0;
+ }
+
+ if (lhsPs.getPkgState().getLatestPackageUseTimeInMills() >
+ rhsPs.getPkgState().getLatestPackageUseTimeInMills()) {
return 1;
- } else if (lhsPkg.getLatestPackageUseTimeInMills() <
- rhsPkg.getLatestPackageUseTimeInMills()) {
+ } else if (lhsPs.getPkgState().getLatestPackageUseTimeInMills() <
+ rhsPs.getPkgState().getLatestPackageUseTimeInMills()) {
return -1;
+ } else if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
+ return 1;
} else {
- final PackageSetting lhsPs = mService.getPackageSetting(
- lhsPkg.getPackageName());
- if (lhsPs == null) {
- return 0;
- }
-
- final PackageSetting rhsPs = mService.getPackageSetting(
- rhsPkg.getPackageName());
- if (rhsPs == null) {
- return 0;
- }
-
- if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
- return 1;
- } else {
- return -1;
- }
+ return -1;
}
}
});
@@ -869,10 +868,9 @@
// TODO(b/135203078): This may be broken due to inner mutability problems that were broken
// as part of moving to PackageInfoUtils. Flags couldn't be determined.
ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(ps.pkg, 0,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
if (addApplicationInfo) {
- return new InstantAppInfo(appInfo,
- requestedPermissions, grantedPermissions);
+ return new InstantAppInfo(appInfo, requestedPermissions, grantedPermissions);
} else {
return new InstantAppInfo(appInfo.packageName,
appInfo.loadLabel(mService.mContext.getPackageManager()),
diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java
index 0a065eb..2d42107 100644
--- a/services/core/java/com/android/server/pm/InstructionSets.java
+++ b/services/core/java/com/android/server/pm/InstructionSets.java
@@ -16,12 +16,14 @@
package com.android.server.pm;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArraySet;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+
import dalvik.system.VMRuntime;
import java.util.ArrayList;
@@ -109,13 +111,4 @@
return VMRuntime.getInstructionSet(abis.primary);
}
-
- public static String getPrimaryInstructionSet(AndroidPackage pkg) {
- if (pkg.getPrimaryCpuAbi() == null) {
- return getPreferredInstructionSet();
- }
-
- return VMRuntime.getInstructionSet(pkg.getPrimaryCpuAbi());
- }
-
}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
index c97d85d..9dc545a 100644
--- a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -17,7 +17,7 @@
package com.android.server.pm;
import android.content.pm.PackageManager;
-import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedIntentInfo;
import android.util.ArraySet;
import android.util.Slog;
@@ -35,7 +35,7 @@
private int mState;
- private ArrayList<ComponentParseUtils.ParsedActivityIntentInfo> mFilters = new ArrayList<>();
+ private ArrayList<ParsedIntentInfo> mFilters = new ArrayList<>();
private ArraySet<String> mHosts = new ArraySet<>();
private int mUserId;
@@ -66,7 +66,7 @@
setState(STATE_VERIFICATION_PENDING);
}
- public ArrayList<ComponentParseUtils.ParsedActivityIntentInfo> getFilters() {
+ public ArrayList<ParsedIntentInfo> getFilters() {
return mFilters;
}
@@ -123,7 +123,7 @@
return false;
}
- public void addFilter(ComponentParseUtils.ParsedActivityIntentInfo filter) {
+ public void addFilter(ParsedIntentInfo filter) {
mFilters.add(filter);
mHosts.addAll(filter.getHostsList());
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index f9cfee1..dabcc35 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -21,14 +21,13 @@
import static com.android.server.pm.PackageManagerService.SCAN_INITIAL;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.AndroidPackage;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Base64;
import android.util.LongSparseArray;
import android.util.Slog;
-import com.android.internal.util.Preconditions;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 09e3feb..8031eaa 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -50,7 +50,6 @@
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.AndroidPackage;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -73,8 +72,10 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.util.ArrayList;
@@ -134,6 +135,8 @@
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final ShortcutChangeHandler mShortcutChangeHandler;
+
private final Handler mCallbackHandler;
private PackageInstallerService mPackageInstallerService;
@@ -153,6 +156,8 @@
mShortcutServiceInternal = Objects.requireNonNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
+ mShortcutChangeHandler = new ShortcutChangeHandler(mUserManagerInternal);
+ mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
}
@@ -720,12 +725,37 @@
@Override
public void registerShortcutChangeCallback(String callingPackage, long changedSince,
String packageName, List shortcutIds, List<LocusId> locusIds,
- ComponentName componentName, int flags, IShortcutChangeCallback callback,
- int callbackId) {
+ ComponentName componentName, int flags, IShortcutChangeCallback callback) {
+ ensureShortcutPermission(callingPackage);
+
+ if (shortcutIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by shortcut ID, package name must also be set");
+ }
+ if (locusIds != null && packageName == null) {
+ throw new IllegalArgumentException(
+ "To query by locus ID, package name must also be set");
+ }
+
+ UserHandle user = UserHandle.of(injectCallingUserId());
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED) {
+ user = null;
+ }
+
+ // TODO: When ShortcutQueryWrapper (ag/10323729) is available, pass that directly.
+ ShortcutChangeHandler.QueryInfo query = new ShortcutChangeHandler.QueryInfo(
+ changedSince, packageName, shortcutIds, locusIds, componentName, flags, user);
+ mShortcutChangeHandler.addShortcutChangeCallback(callback, query);
}
@Override
- public void unregisterShortcutChangeCallback(String callingPackage, int callbackId) {
+ public void unregisterShortcutChangeCallback(String callingPackage,
+ IShortcutChangeCallback callback) {
+ ensureShortcutPermission(callingPackage);
+
+ mShortcutChangeHandler.removeShortcutChangeCallback(callback);
}
@Override
@@ -1005,6 +1035,153 @@
mCallbackHandler.post(r);
}
+ public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
+
+ static class QueryInfo {
+ final long mChangedSince;
+ final String mPackage;
+ final List<String> mShortcutIds;
+ final List<LocusId> mLocusIds;
+ final ComponentName mActivity;
+ final int mQueryFlags;
+ final UserHandle mCallbackUser;
+
+ QueryInfo(long changedSince, String packageName, List<String> shortcutIds,
+ List<LocusId> locusIds, ComponentName activity, int flags,
+ UserHandle callbackUser) {
+ mChangedSince = changedSince;
+ mPackage = packageName;
+ mShortcutIds = shortcutIds;
+ mLocusIds = locusIds;
+ mActivity = activity;
+ mQueryFlags = flags;
+ mCallbackUser = callbackUser;
+ }
+ }
+
+ private final UserManagerInternal mUserManagerInternal;
+
+ ShortcutChangeHandler(UserManagerInternal userManager) {
+ mUserManagerInternal = userManager;
+ }
+
+ private final RemoteCallbackList<IShortcutChangeCallback> mCallbacks =
+ new RemoteCallbackList<>();
+
+ public synchronized void addShortcutChangeCallback(IShortcutChangeCallback callback,
+ QueryInfo query) {
+ mCallbacks.unregister(callback);
+ mCallbacks.register(callback, query);
+ }
+
+ public synchronized void removeShortcutChangeCallback(
+ IShortcutChangeCallback callback) {
+ mCallbacks.unregister(callback);
+ }
+
+ @Override
+ public void onShortcutsAddedOrUpdated(String packageName, List<ShortcutInfo> shortcuts,
+ UserHandle user) {
+ onShortcutEvent(packageName, shortcuts, user, false);
+ }
+
+ @Override
+ public void onShortcutsRemoved(String packageName, List<ShortcutInfo> shortcuts,
+ UserHandle user) {
+ onShortcutEvent(packageName, shortcuts, user, true);
+ }
+
+ private void onShortcutEvent(String packageName,
+ List<ShortcutInfo> shortcuts, UserHandle user, boolean shortcutsRemoved) {
+ int count = mCallbacks.beginBroadcast();
+
+ for (int i = 0; i < count; i++) {
+ final IShortcutChangeCallback callback = mCallbacks.getBroadcastItem(i);
+ final QueryInfo query = (QueryInfo) mCallbacks.getBroadcastCookie(i);
+
+ if (query.mCallbackUser != null && !hasUserAccess(query.mCallbackUser, user)) {
+ // Callback owner does not have access to the shortcuts' user.
+ continue;
+ }
+
+ // Filter the list by query, if any matches exists, send via callback.
+ List<ShortcutInfo> matchedList =
+ filterShortcutsByQuery(packageName, shortcuts, query);
+ if (!CollectionUtils.isEmpty(matchedList)) {
+ try {
+ if (shortcutsRemoved) {
+ callback.onShortcutsRemoved(packageName, matchedList, user);
+ } else {
+ callback.onShortcutsAddedOrUpdated(packageName, matchedList, user);
+ }
+ } catch (RemoteException e) {
+ // The RemoteCallbackList will take care of removing the dead object.
+ }
+ }
+ }
+
+ mCallbacks.finishBroadcast();
+ }
+
+ public static List<ShortcutInfo> filterShortcutsByQuery(String packageName,
+ List<ShortcutInfo> shortcuts, QueryInfo query) {
+ if (query.mPackage != null && query.mPackage != packageName) {
+ return null;
+ }
+
+ List<ShortcutInfo> matches = new ArrayList<>();
+
+ final boolean matchDynamic =
+ (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
+ final boolean matchPinned =
+ (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
+ final boolean matchManifest =
+ (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
+ final boolean matchCached =
+ (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
+ final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
+ | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
+ | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
+ | (matchCached ? ShortcutInfo.FLAG_CACHED : 0);
+
+ for (int i = 0; i < shortcuts.size(); i++) {
+ final ShortcutInfo si = shortcuts.get(i);
+
+ if (query.mActivity != null && !query.mActivity.equals(si.getActivity())) {
+ continue;
+ }
+
+ if (query.mChangedSince != 0
+ && query.mChangedSince > si.getLastChangedTimestamp()) {
+ continue;
+ }
+
+ if (query.mShortcutIds != null && !query.mShortcutIds.contains(si.getId())) {
+ continue;
+ }
+
+ if (query.mLocusIds != null && !query.mLocusIds.contains(si.getLocusId())) {
+ continue;
+ }
+
+ if ((shortcutFlags & si.getFlags()) != 0) {
+ matches.add(si);
+ }
+ }
+
+ return matches;
+ }
+
+ private boolean hasUserAccess(UserHandle callbackUser, UserHandle shortcutUser) {
+ final int callbackUserId = callbackUser.getIdentifier();
+ final int shortcutUserId = shortcutUser.getIdentifier();
+
+ if (shortcutUser == callbackUser) return true;
+ return mUserManagerInternal.isProfileAccessible(callbackUserId, shortcutUserId,
+ null, false);
+ }
+ }
+
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
// TODO Simplify with lambdas.
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index ae7a4a7..2df4a92 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.IOtaDexopt;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -35,13 +34,17 @@
import com.android.internal.logging.MetricsLogger;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
@@ -118,31 +121,33 @@
if (mDexoptCommands != null) {
throw new IllegalStateException("already called prepare()");
}
- final List<AndroidPackage> important;
- final List<AndroidPackage> others;
+ final List<PackageSetting> important;
+ final List<PackageSetting> others;
synchronized (mPackageManagerService.mLock) {
// Important: the packages we need to run with ab-ota compiler-reason.
important = PackageManagerServiceUtils.getPackagesForDexopt(
- mPackageManagerService.mPackages.values(), mPackageManagerService,
+ mPackageManagerService.mSettings.mPackages.values(), mPackageManagerService,
DEBUG_DEXOPT);
// Others: we should optimize this with the (first-)boot compiler-reason.
- others = new ArrayList<>(mPackageManagerService.mPackages.values());
+ others = new ArrayList<>(mPackageManagerService.mSettings.mPackages.values());
others.removeAll(important);
+ others.removeIf(PackageManagerServiceUtils.REMOVE_IF_NULL_PKG);
// Pre-size the array list by over-allocating by a factor of 1.5.
mDexoptCommands = new ArrayList<>(3 * mPackageManagerService.mPackages.size() / 2);
}
- for (AndroidPackage p : important) {
- mDexoptCommands.addAll(generatePackageDexopts(p, PackageManagerService.REASON_AB_OTA));
+ for (PackageSetting pkgSetting : important) {
+ mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting,
+ PackageManagerService.REASON_AB_OTA));
}
- for (AndroidPackage p : others) {
+ for (PackageSetting pkgSetting : others) {
// We assume here that there are no core apps left.
- if (p.isCoreApp()) {
+ if (pkgSetting.pkg.isCoreApp()) {
throw new IllegalStateException("Found a core app that's not important");
}
- mDexoptCommands.addAll(
- generatePackageDexopts(p, PackageManagerService.REASON_FIRST_BOOT));
+ mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting,
+ PackageManagerService.REASON_FIRST_BOOT));
}
completeSize = mDexoptCommands.size();
@@ -150,8 +155,8 @@
if (spaceAvailable < BULK_DELETE_THRESHOLD) {
Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
+ PackageManagerServiceUtils.packagesToString(others));
- for (AndroidPackage pkg : others) {
- mPackageManagerService.deleteOatArtifactsOfPackage(pkg.getPackageName());
+ for (PackageSetting pkg : others) {
+ mPackageManagerService.deleteOatArtifactsOfPackage(pkg.name);
}
}
long spaceAvailableNow = getAvailableSpace();
@@ -161,16 +166,18 @@
if (DEBUG_DEXOPT) {
try {
// Output some data about the packages.
- AndroidPackage lastUsed = Collections.max(important,
- (pkg1, pkg2) -> Long.compare(
- pkg1.getLatestForegroundPackageUseTimeInMills(),
- pkg2.getLatestForegroundPackageUseTimeInMills()));
+ PackageSetting lastUsed = Collections.max(important,
+ (pkgSetting1, pkgSetting2) -> Long.compare(
+ pkgSetting1.getPkgState()
+ .getLatestForegroundPackageUseTimeInMills(),
+ pkgSetting2.getPkgState()
+ .getLatestForegroundPackageUseTimeInMills()));
Log.d(TAG, "A/B OTA: lastUsed time = "
- + lastUsed.getLatestForegroundPackageUseTimeInMills());
+ + lastUsed.getPkgState().getLatestForegroundPackageUseTimeInMills());
Log.d(TAG, "A/B OTA: deprioritized packages:");
- for (AndroidPackage pkg : others) {
- Log.d(TAG, " " + pkg.getPackageName() + " - "
- + pkg.getLatestForegroundPackageUseTimeInMills());
+ for (PackageSetting pkgSetting : others) {
+ Log.d(TAG, " " + pkgSetting.name + " - "
+ + pkgSetting.getPkgState().getLatestForegroundPackageUseTimeInMills());
}
} catch (Exception ignored) {
}
@@ -263,7 +270,7 @@
* Generate all dexopt commands for the given package.
*/
private synchronized List<String> generatePackageDexopts(AndroidPackage pkg,
- int compilationReason) {
+ PackageSetting pkgSetting, int compilationReason) {
// Intercept and collect dexopt requests
final List<String> commands = new ArrayList<String>();
final Installer collectingInstaller = new Installer(mContext, true) {
@@ -333,7 +340,7 @@
PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
collectingInstaller, mPackageManagerService.mInstallLock, mContext);
- optimizer.performDexOpt(pkg,
+ optimizer.performDexOpt(pkg, pkgSetting,
null /* ISAs */,
null /* CompilerStats.PackageStats */,
mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(
@@ -386,9 +393,12 @@
continue;
}
- final String[] instructionSets = getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
- pkg.getSecondaryCpuAbi());
- final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
+ PackageSetting pkgSetting = mPackageManagerService.getPackageSetting(pkg.getPackageName());
+ final String[] instructionSets = getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
+ final List<String> paths =
+ AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (String path : paths) {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index d7c161c..e355bb9 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -17,11 +17,12 @@
package com.android.server.pm;
import android.annotation.Nullable;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ParsedPackage;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
import java.util.Set;
@@ -33,7 +34,8 @@
* Derive and get the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
- NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, File appLib32InstallDir);
+ NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
+ File appLib32InstallDir);
/**
* Calculate the abis for a bundled app. These can uniquely be determined from the contents of
@@ -48,9 +50,8 @@
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
- Pair<Abis, NativeLibraryPaths> derivePackageAbi(
- AndroidPackage pkg, String cpuAbiOverride, boolean extractLibs)
- throws PackageManagerException;
+ Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp,
+ String cpuAbiOverride, boolean extractLibs) throws PackageManagerException;
/**
* Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all
@@ -113,8 +114,9 @@
this.secondary = secondary;
}
- Abis(AndroidPackage pkg) {
- this(pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi());
+ Abis(AndroidPackage pkg, PackageSetting pkgSetting) {
+ this(AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
}
public void applyTo(ParsedPackage pkg) {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 482fc49..0bd8b28 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -27,9 +27,7 @@
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
@@ -40,6 +38,8 @@
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import dalvik.system.VMRuntime;
@@ -131,11 +131,11 @@
}
@Override
- public NativeLibraryPaths getNativeLibraryPaths(
- AndroidPackage pkg, File appLib32InstallDir) {
- return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.getCodePath(),
- pkg.getBaseCodePath(), pkg.isSystemApp(),
- pkg.isUpdatedSystemApp());
+ public NativeLibraryPaths getNativeLibraryPaths(AndroidPackage pkg, PackageSetting pkgSetting,
+ File appLib32InstallDir) {
+ return getNativeLibraryPaths(new Abis(pkg, pkgSetting), appLib32InstallDir,
+ pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
+ pkgSetting.getPkgState().isUpdatedSystemApp());
}
private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis,
@@ -273,7 +273,7 @@
// ABI that's higher on the list, i.e, a device that's configured to prefer
// 64 bit apps will see a 64 bit primary ABI,
- if ((pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) == 0) {
+ if (!pkg.isMultiArch()) {
Slog.e(PackageManagerService.TAG,
"Package " + pkg + " has multiple bundled libs, but is not multiarch.");
}
@@ -293,18 +293,21 @@
}
@Override
- public Pair<Abis, NativeLibraryPaths> derivePackageAbi(
- AndroidPackage pkg, String cpuAbiOverride, boolean extractLibs)
+ public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg,
+ boolean isUpdatedSystemApp, String cpuAbiOverride, boolean extractLibs)
throws PackageManagerException {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
- final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg),
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(pkg);
+ String pkgRawSecondaryCpuAbi = AndroidPackageUtils.getRawSecondaryCpuAbi(pkg);
+ final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(
+ new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
PackageManagerService.sAppLib32InstallDir, pkg.getCodePath(),
- pkg.getBaseCodePath(), pkg.isSystemApp(),
- pkg.isUpdatedSystemApp());
+ pkg.getBaseCodePath(), pkg.isSystem(),
+ isUpdatedSystemApp);
// We shouldn't attempt to extract libs from system app when it was not updated.
- if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
+ if (pkg.isSystem() && !isUpdatedSystemApp) {
extractLibs = false;
}
@@ -317,7 +320,7 @@
NativeLibraryHelper.Handle handle = null;
try {
- handle = NativeLibraryHelper.Handle.create(pkg);
+ handle = AndroidPackageUtils.createNativeLibraryHandle(pkg);
// TODO(multiArch): This can be null for apps that didn't go through the
// usual installation process. We can calculate it again, like we
// do during install time.
@@ -329,17 +332,7 @@
// Null out the abis so that they can be recalculated.
primaryCpuAbi = null;
secondaryCpuAbi = null;
- if ((pkg.getFlags() & ApplicationInfo.FLAG_MULTIARCH) != 0) {
- // Warn if we've set an abiOverride for multi-lib packages..
- // By definition, we need to copy both 32 and 64 bit libraries for
- // such packages.
- if (pkg.getCpuAbiOverride() != null
- && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(
- pkg.getCpuAbiOverride())) {
- Slog.w(PackageManagerService.TAG,
- "Ignoring abiOverride for multi arch application.");
- }
-
+ if (pkg.isMultiArch()) {
int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
@@ -357,7 +350,7 @@
}
// Shared library native code should be in the APK zip aligned
- if (abi32 >= 0 && pkg.isLibrary() && extractLibs) {
+ if (abi32 >= 0 && AndroidPackageUtils.isLibrary(pkg) && extractLibs) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library native lib extraction not supported");
}
@@ -384,7 +377,7 @@
if (abi64 >= 0) {
// Shared library native libs should be in the APK zip aligned
- if (extractLibs && pkg.isLibrary()) {
+ if (extractLibs && AndroidPackageUtils.isLibrary(pkg)) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library native lib extraction not supported");
}
@@ -437,7 +430,7 @@
if (copyRet >= 0) {
// Shared libraries that have native libs must be multi-architecture
- if (pkg.isLibrary()) {
+ if (AndroidPackageUtils.isLibrary(pkg)) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library with native libs must be multiarch");
}
@@ -461,9 +454,8 @@
final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
return new Pair<>(abis,
getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir,
- pkg.getCodePath(), pkg.getBaseCodePath(),
- pkg.isSystemApp(),
- pkg.isUpdatedSystemApp()));
+ pkg.getCodePath(), pkg.getBaseCodePath(), pkg.isSystem(),
+ isUpdatedSystemApp));
}
/**
@@ -484,9 +476,11 @@
public String getAdjustedAbiForSharedUser(
Set<PackageSetting> packagesForUser, AndroidPackage scannedPackage) {
String requiredInstructionSet = null;
- if (scannedPackage != null && scannedPackage.getPrimaryCpuAbi() != null) {
- requiredInstructionSet = VMRuntime.getInstructionSet(
- scannedPackage.getPrimaryCpuAbi());
+ if (scannedPackage != null) {
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);
+ if (pkgRawPrimaryCpuAbi != null) {
+ requiredInstructionSet = VMRuntime.getInstructionSet(pkgRawPrimaryCpuAbi);
+ }
}
PackageSetting requirer = null;
@@ -533,7 +527,7 @@
} else {
// requirer == null implies that we're updating all ABIs in the set to
// match scannedPackage.
- adjustedAbi = scannedPackage.getPrimaryCpuAbi();
+ adjustedAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);
}
return adjustedAbi;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2b42221..e625aef 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -38,13 +38,13 @@
import static dalvik.system.DexFile.getSafeModeCompilerFilter;
import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
-import android.content.pm.parsing.AndroidPackage;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -63,6 +63,8 @@
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import dalvik.system.DexFile;
@@ -112,7 +114,7 @@
static boolean canOptimizePackage(AndroidPackage pkg) {
// We do not dexopt a package with no code.
- if ((pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) == 0) {
+ if (!pkg.isHasCode()) {
return false;
}
@@ -126,7 +128,7 @@
* <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
* synchronized on {@link #mInstallLock}.
*/
- int performDexOpt(AndroidPackage pkg,
+ int performDexOpt(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
String[] instructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
if (pkg.getUid() == -1) {
@@ -139,7 +141,7 @@
synchronized (mInstallLock) {
final long acquireTime = acquireWakeLockLI(pkg.getUid());
try {
- return performDexOptLI(pkg, instructionSets,
+ return performDexOptLI(pkg, pkgSetting, instructionSets,
packageStats, packageUseInfo, options);
} finally {
releaseWakeLockLI(acquireTime);
@@ -152,19 +154,21 @@
* It assumes the install lock is held.
*/
@GuardedBy("mInstallLock")
- private int performDexOptLI(AndroidPackage pkg,
+ private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
- final List<SharedLibraryInfo> sharedLibraries = pkg.getUsesLibraryInfos();
+ final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getPkgState()
+ .getUsesLibraryInfos();
final String[] instructionSets = targetInstructionSets != null ?
- targetInstructionSets : getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
- pkg.getSecondaryCpuAbi());
+ targetInstructionSets : getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
- final List<String> paths = pkg.getAllCodePaths();
+ final List<String> paths = AndroidPackageUtils.getAllCodePaths(pkg);
int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
if (sharedGid == -1) {
- Slog.wtf(TAG, "Well this is awkward; package " + pkg.getAppInfoName() + " had UID "
+ Slog.wtf(TAG, "Well this is awkward; package " + pkg.getPackageName() + " had UID "
+ pkg.getUid(), new Throwable());
sharedGid = android.os.Process.NOBODY_UID;
}
@@ -173,7 +177,7 @@
// For each code path in the package, this array contains the class loader context that
// needs to be passed to dexopt in order to ensure correct optimizations.
boolean[] pathsWithCode = new boolean[paths.size()];
- pathsWithCode[0] = (pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ pathsWithCode[0] = pkg.isHasCode();
for (int i = 1; i < paths.size(); i++) {
pathsWithCode[i] = (pkg.getSplitFlags()[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
}
@@ -232,10 +236,10 @@
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
// flags.
- final int dexoptFlags = getDexFlags(pkg, compilerFilter, options);
+ final int dexoptFlags = getDexFlags(pkg, pkgSetting, compilerFilter, options);
for (String dexCodeIsa : dexCodeInstructionSets) {
- int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
+ int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
packageStats, options.isDowngrade(), profileName, dexMetadataPath,
options.getCompilationReason());
@@ -260,8 +264,8 @@
* DEX_OPT_SKIPPED if the path does not need to be deopt-ed.
*/
@GuardedBy("mInstallLock")
- private int dexOptPath(AndroidPackage pkg, String path, String isa,
- String compilerFilter, boolean profileUpdated, String classLoaderContext,
+ private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path,
+ String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
String profileName, String dexMetadataPath, int compilationReason) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
@@ -270,10 +274,11 @@
return DEX_OPT_SKIPPED;
}
- String oatDir = getPackageOatDirIfSupported(pkg);
+ String oatDir = getPackageOatDirIfSupported(pkg,
+ pkgSetting.getPkgState().isUpdatedSystemApp());
Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
- + " pkg=" + pkg.getAppInfoPackageName() + " isa=" + isa
+ + " pkg=" + pkg.getPackageName() + " isa=" + isa
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " targetFilter=" + compilerFilter + " oatDir=" + oatDir
+ " classLoaderContext=" + classLoaderContext);
@@ -284,9 +289,10 @@
// TODO: Consider adding 2 different APIs for primary and secondary dexopt.
// installd only uses downgrade flag for secondary dex files and ignores it for
// primary dex files.
+ String seInfo = AndroidPackageUtils.getSeInfo(pkg, pkgSetting);
mInstaller.dexopt(path, uid, pkg.getPackageName(), isa, dexoptNeeded, oatDir,
dexoptFlags, compilerFilter, pkg.getVolumeUuid(), classLoaderContext,
- pkg.getSeInfo(), false /* downgrade*/, pkg.getTargetSdkVersion(),
+ seInfo, false /* downgrade*/, pkg.getTargetSdkVersion(),
profileName, dexMetadataPath,
getAugmentedReasonName(compilationReason, dexMetadataPath != null));
@@ -449,13 +455,14 @@
/**
* Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
*/
- void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
+ void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg, PackageSetting pkgSetting,
PackageDexUsage.PackageUseInfo useInfo) {
- final String[] instructionSets = getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
- pkg.getSecondaryCpuAbi());
+ final String[] instructionSets = getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
- final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
+ final List<String> paths = AndroidPackageUtils.getAllCodePathsExcludingResourceOnly(pkg);
for (String path : paths) {
pw.println("path: " + path);
@@ -546,7 +553,7 @@
private String getRealCompilerFilter(AndroidPackage pkg, String targetCompilerFilter,
boolean isUsedByOtherApps) {
// When an app or priv app is configured to run out of box, only verify it.
- if (pkg.isEmbeddedDexUsed()
+ if (pkg.isUseEmbeddedDex()
|| (pkg.isPrivileged()
&& DexManager.isPackageSelectedToRunOob(pkg.getPackageName()))) {
return "verify";
@@ -562,8 +569,7 @@
// PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages
// but that would have the downside of possibly producing a big odex files which would
// be ignored anyway.
- boolean vmSafeModeOrDebuggable = ((pkg.getFlags() & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0)
- || ((pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+ boolean vmSafeModeOrDebuggable = pkg.isVmSafeMode() || pkg.isDebuggable();
if (vmSafeModeOrDebuggable) {
return getSafeModeCompilerFilter(targetCompilerFilter);
@@ -583,14 +589,15 @@
}
private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
- return getDexFlags(info.flags, info.getHiddenApiEnforcementPolicy(),
- info.splitDependencies, info.requestsIsolatedSplitLoading(), compilerFilter,
- options);
+ return getDexFlags((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0,
+ info.getHiddenApiEnforcementPolicy(), info.splitDependencies,
+ info.requestsIsolatedSplitLoading(), compilerFilter, options);
}
- private int getDexFlags(AndroidPackage pkg, String compilerFilter,
- DexoptOptions options) {
- return getDexFlags(pkg.getFlags(), pkg.getHiddenApiEnforcementPolicy(),
- pkg.getSplitDependencies(), pkg.requestsIsolatedSplitLoading(), compilerFilter,
+ private int getDexFlags(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
+ String compilerFilter, DexoptOptions options) {
+ return getDexFlags(pkg.isDebuggable(),
+ AndroidPackageUtils.getHiddenApiEnforcementPolicy(pkg, pkgSetting),
+ pkg.getSplitDependencies(), pkg.isIsolatedSplitLoading(), compilerFilter,
options);
}
@@ -598,10 +605,9 @@
* Computes the dex flags that needs to be pass to installd for the given package and compiler
* filter.
*/
- private int getDexFlags(int flags, int hiddenApiEnforcementPolicy,
+ private int getDexFlags(boolean debuggable, int hiddenApiEnforcementPolicy,
SparseArray<int[]> splitDependencies, boolean requestsIsolatedSplitLoading,
String compilerFilter, DexoptOptions options) {
- boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public unles they are based
// on profiles from dex metadata archives.
// The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
@@ -699,8 +705,8 @@
* not needed or unsupported for the package.
*/
@Nullable
- private String getPackageOatDirIfSupported(AndroidPackage pkg) {
- if (!pkg.canHaveOatDir()) {
+ private String getPackageOatDirIfSupported(AndroidPackage pkg, boolean isUpdatedSystemApp) {
+ if (!AndroidPackageUtils.canHaveOatDir(pkg, isUpdatedSystemApp)) {
return null;
}
File codePath = new File(pkg.getCodePath());
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0e554f8..41988d6 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,7 +81,6 @@
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
-import android.content.pm.parsing.AndroidPackage;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -127,6 +126,7 @@
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.security.VerityUtils;
import libcore.io.IoUtils;
@@ -1169,12 +1169,13 @@
*/
private static boolean isIncrementalInstallationAllowed(String packageName) {
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- final AndroidPackage existingPackage = pmi.getPackage(packageName);
- if (existingPackage == null) {
+ final PackageSetting existingPkgSetting = pmi.getPackageSetting(packageName);
+ if (existingPkgSetting == null || existingPkgSetting.pkg == null) {
return true;
}
- return !PackageManagerService.isSystemApp(existingPackage);
+ return !existingPkgSetting.pkg.isSystem()
+ && !existingPkgSetting.getPkgState().isUpdatedSystemApp();
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2a3f7ed..14964b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -104,6 +104,9 @@
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.internal.util.ArrayUtils.emptyIfNull;
import static com.android.internal.util.ArrayUtils.filter;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME;
import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -209,20 +212,17 @@
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ApkParseUtils;
-import android.content.pm.parsing.ComponentParseUtils;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
-import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.PackageInfoUtils;
-import android.content.pm.parsing.ParsedPackage;
-import android.content.pm.parsing.library.PackageBackwardCompatibility;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
import android.content.res.Resources;
import android.content.rollback.IRollbackManager;
import android.database.ContentObserver;
@@ -341,6 +341,13 @@
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.dex.ViewCompiler;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -1011,13 +1018,13 @@
private final AppsFilter mAppsFilter;
- class PackageParserCallback implements PackageParser.Callback {
+ class PackageParserCallback extends PackageParser2.Callback {
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
}
}
- final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
+ final PackageParser2.Callback mPackageParserCallback = new PackageParserCallback();
// Currently known shared libraries.
final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
@@ -1080,7 +1087,7 @@
boolean mResolverReplaced = false;
private final @Nullable ComponentName mIntentFilterVerifierComponent;
- private final @Nullable IntentFilterVerifier<ParsedActivityIntentInfo> mIntentFilterVerifier;
+ private final @Nullable IntentFilterVerifier<ParsedIntentInfo> mIntentFilterVerifier;
private int mIntentFilterVerificationToken = 0;
@@ -1141,7 +1148,7 @@
void receiveVerificationResponse(int verificationId);
}
- private class IntentVerifierProxy implements IntentFilterVerifier<ParsedActivityIntentInfo> {
+ private class IntentVerifierProxy implements IntentFilterVerifier<ParsedIntentInfo> {
private Context mContext;
private ComponentName mIntentFilterVerifierComponent;
private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<>();
@@ -1166,11 +1173,11 @@
String packageName = ivs.getPackageName();
- ArrayList<ParsedActivityIntentInfo> filters = ivs.getFilters();
+ ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
final int filterCount = filters.size();
ArraySet<String> domainsSet = new ArraySet<>();
for (int m=0; m<filterCount; m++) {
- ParsedActivityIntentInfo filter = filters.get(m);
+ ParsedIntentInfo filter = filters.get(m);
domainsSet.addAll(filter.getHostsList());
}
synchronized (mLock) {
@@ -1222,14 +1229,14 @@
final boolean verified = ivs.isVerified();
- ArrayList<ParsedActivityIntentInfo> filters = ivs.getFilters();
+ ArrayList<ParsedIntentInfo> filters = ivs.getFilters();
final int count = filters.size();
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.i(TAG, "Received verification response " + verificationId
+ " for " + count + " filters, verified=" + verified);
}
for (int n=0; n<count; n++) {
- ParsedActivityIntentInfo filter = filters.get(n);
+ ParsedIntentInfo filter = filters.get(n);
filter.setVerified(verified);
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "IntentFilter " + filter.toString()
@@ -1342,7 +1349,7 @@
@Override
public boolean addOneIntentFilterVerification(int verifierUid, int userId, int verificationId,
- ParsedActivityIntentInfo filter, String packageName) {
+ ParsedIntentInfo filter, String packageName) {
if (!hasValidDomains(filter)) {
return false;
}
@@ -1371,7 +1378,7 @@
}
}
- private static boolean hasValidDomains(ParsedActivityIntentInfo filter) {
+ private static boolean hasValidDomains(ParsedIntentInfo filter) {
return filter.hasCategory(Intent.CATEGORY_BROWSABLE)
&& (filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
filter.hasDataScheme(IntentFilter.SCHEME_HTTPS));
@@ -2033,7 +2040,7 @@
mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
}
- final String packageName = res.pkg.getAppInfoPackageName();
+ final String packageName = res.pkg.getPackageName();
// Determine the set of users who are adding this package for
// the first time vs. those who are seeing an update.
@@ -2083,7 +2090,7 @@
// Send added for users that see the package for the first time
// sendPackageAddedForNewUsers also deals with system apps
int appId = UserHandle.getAppId(res.uid);
- boolean isSystem = res.pkg.isSystemApp();
+ boolean isSystem = res.pkg.isSystem();
sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload,
virtualPreload /*startReceiver*/, appId, firstUserIds, firstInstantUserIds);
@@ -2147,7 +2154,7 @@
null /*package*/, null /*extras*/, 0 /*flags*/,
packageName /*targetPackage*/,
null /*finishedReceiver*/, updateUserIds, instantUserIds);
- } else if (launchedForRestore && !isSystemApp(res.pkg)) {
+ } else if (launchedForRestore && !res.pkg.isSystem()) {
// First-install and we did a restore, so we're responsible for the
// first-launch broadcast.
if (DEBUG_BACKUP) {
@@ -2159,14 +2166,14 @@
}
// Send broadcast package appeared if external for all users
- if (isExternal(res.pkg)) {
+ if (res.pkg.isExternalStorage()) {
if (!update) {
final StorageManager storage = mInjector.getStorageManager();
VolumeInfo volume =
storage.findVolumeByUuid(
res.pkg.getStorageUuid().toString());
int packageExternalStorageType =
- getPackageExternalStorageType(volume, isExternal(res.pkg));
+ getPackageExternalStorageType(volume, res.pkg.isExternalStorage());
// If the package was installed externally, log it.
if (packageExternalStorageType != StorageEnums.UNKNOWN) {
FrameworkStatsLog.write(
@@ -2499,15 +2506,18 @@
packageName -> {
synchronized (m.mInstallLock) {
final AndroidPackage pkg;
+ final PackageSetting ps;
final SharedUserSetting sharedUser;
+ final String oldSeInfo;
synchronized (m.mLock) {
- PackageSetting ps = m.mSettings.getPackageLPr(packageName);
+ ps = m.mSettings.getPackageLPr(packageName);
if (ps == null) {
Slog.e(TAG, "Failed to find package setting " + packageName);
return;
}
pkg = ps.pkg;
- sharedUser = ps.sharedUser;
+ sharedUser = ps.getSharedUser();
+ oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
}
if (pkg == null) {
@@ -2517,10 +2527,10 @@
final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
m.mInjector.getCompatibility());
- if (!newSeInfo.equals(pkg.getSeInfo())) {
+ if (!newSeInfo.equals(oldSeInfo)) {
Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
- + pkg.getSeInfo() + " to: " + newSeInfo);
- pkg.mutate().setSeInfo(newSeInfo);
+ + oldSeInfo + " to: " + newSeInfo);
+ ps.getPkgState().setOverrideSeInfo(newSeInfo);
m.prepareAppDataAfterInstallLIF(pkg);
}
}
@@ -2859,12 +2869,8 @@
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
- PackageParser packageParser = new PackageParser();
- packageParser.setSeparateProcesses(mSeparateProcesses);
- packageParser.setOnlyCoreApps(mOnlyCore);
- packageParser.setDisplayMetrics(mMetrics);
- packageParser.setCacheDir(mCacheDir);
- packageParser.setCallback(mPackageParserCallback);
+ PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore,
+ mMetrics, mCacheDir, mPackageParserCallback);
ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// Collect vendor/product/system_ext overlay packages. (Do this before scanning
@@ -2902,7 +2908,9 @@
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
- mOverlayConfig = OverlayConfig.initializeSystemInstance(mPmInternal::forEachPackage);
+ mOverlayConfig = OverlayConfig.initializeSystemInstance(
+ consumer -> mPmInternal.forEachPackage(
+ pkg -> consumer.accept(pkg, pkg.isSystem())));
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -2995,8 +3003,11 @@
+ (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+ " , cached: " + cachedSystemApps);
if (mIsUpgrade && systemPackagesCount > 0) {
- MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",
- ((int) systemScanTime) / systemPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
+ systemScanTime / systemPackagesCount);
+ //CHECKSTYLE:ON IndentationCheck
}
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
@@ -3044,7 +3055,7 @@
// special privileges
removePackageLI(pkg, true);
try {
- final File codePath = new File(pkg.getAppInfoCodePath());
+ final File codePath = new File(pkg.getCodePath());
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
@@ -3123,8 +3134,12 @@
+ (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ " , cached: " + cachedNonSystemApps);
if (mIsUpgrade && dataPackagesCount > 0) {
- MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
- ((int) dataScanTime) / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+ dataScanTime / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
}
}
mExpectingBetter.clear();
@@ -3149,7 +3164,7 @@
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
- updateAllSharedLibrariesLocked(null, Collections.unmodifiableMap(mPackages));
+ updateAllSharedLibrariesLocked(null, null, Collections.unmodifiableMap(mPackages));
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
// NOTE: We ignore potential failures here during a system scan (like
@@ -3177,7 +3192,7 @@
// Now that we know all the packages we are keeping,
// read and update their last usage times.
- mPackageUsage.read(mPackages);
+ mPackageUsage.read(mSettings.mPackages);
mCompilerStats.read();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
@@ -3385,8 +3400,10 @@
}
mDexManager.load(userPackages);
if (mIsUpgrade) {
- MetricsLogger.histogram(null, "ota_package_manager_init_time",
- (int) (SystemClock.uptimeMillis() - startTime));
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME,
+ SystemClock.uptimeMillis() - startTime);
}
} // synchronized (mLock)
} // synchronized (mInstallLock)
@@ -3476,7 +3493,8 @@
* APK will be installed and the package will be disabled. To recover from this situation,
* the user will need to go into system settings and re-enable the package.
*/
- private boolean enableCompressedPackage(AndroidPackage stubPkg) {
+ private boolean enableCompressedPackage(AndroidPackage stubPkg,
+ @NonNull PackageSetting stubPkgSetting) {
final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE;
synchronized (mInstallLock) {
@@ -3487,7 +3505,7 @@
synchronized (mLock) {
prepareAppDataAfterInstallLIF(pkg);
try {
- updateSharedLibrariesLocked(pkg, null,
+ updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
Collections.unmodifiableMap(mPackages));
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
@@ -4010,15 +4028,13 @@
}
ArraySet<String> domains = null;
- if (pkg.getActivities() != null) {
- for (ParsedActivity a : pkg.getActivities()) {
- for (ParsedActivityIntentInfo filter : a.intents) {
- if (hasValidDomains(filter)) {
- if (domains == null) {
- domains = new ArraySet<>();
- }
- domains.addAll(filter.getHostsList());
+ for (ParsedActivity a : pkg.getActivities()) {
+ for (ParsedIntentInfo filter : a.getIntents()) {
+ if (hasValidDomains(filter)) {
+ if (domains == null) {
+ domains = new ArraySet<>();
}
+ domains.addAll(filter.getHostsList());
}
}
}
@@ -4146,7 +4162,7 @@
? Collections.emptySet() : permissionsState.getPermissions(userId);
PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
+ ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps);
if (packageInfo == null) {
return null;
@@ -4208,7 +4224,7 @@
throw new SecurityException("Package " + packageName + " is currently frozen!");
}
- if (!userKeyUnlocked && !ps.pkg.isEncryptionAware()) {
+ if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.pkg)) {
throw new SecurityException("Package " + packageName + " is not encryption aware!");
}
}
@@ -4289,7 +4305,7 @@
}
AndroidPackage p = mPackages.get(packageName);
- if (matchFactoryOnly && p != null && !isSystemApp(p)) {
+ if (matchFactoryOnly && p != null && !p.isSystem()) {
return null;
}
if (DEBUG_PACKAGE_INFO)
@@ -4344,9 +4360,9 @@
return false;
}
final boolean visibleToInstantApp =
- (activity.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
- (activity.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
+ (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && explicitlyVisibleToInstantApp;
} else if (type == TYPE_RECEIVER) {
final ParsedActivity activity = mComponentResolver.getReceiver(component);
@@ -4354,20 +4370,18 @@
return false;
}
final boolean visibleToInstantApp =
- (activity.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
+ (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
final boolean explicitlyVisibleToInstantApp =
- (activity.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
+ (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
return visibleToInstantApp && !explicitlyVisibleToInstantApp;
} else if (type == TYPE_SERVICE) {
final ParsedService service = mComponentResolver.getService(component);
return service != null
- ? (service.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
- : false;
+ && (service.getFlags() & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
} else if (type == TYPE_PROVIDER) {
final ParsedProvider provider = mComponentResolver.getProvider(component);
return provider != null
- ? (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0
- : false;
+ && (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
} else if (type == TYPE_UNKNOWN) {
return isComponentVisibleToInstantApp(component);
}
@@ -4574,7 +4588,7 @@
// reader
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
- if (p != null && p.isMatch(flags)) {
+ if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
PackageSetting ps = getPackageSettingInternal(p.getPackageName(), callingUid);
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return -1;
@@ -4604,7 +4618,7 @@
// reader
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
- if (p != null && p.isMatch(flags)) {
+ if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
PackageSetting ps = getPackageSetting(p.getPackageName());
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
return null;
@@ -4656,7 +4670,7 @@
return null;
}
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, flags,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
@@ -4708,7 +4722,7 @@
}
// Note: isEnabledLP() does not apply here - always return info
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
- p, flags, ps.readUserState(userId), userId);
+ p, flags, ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
}
@@ -5092,7 +5106,7 @@
return null;
}
return PackageInfoUtils.generateActivityInfo(pkg,
- a, flags, ps.readUserState(userId), userId);
+ a, flags, ps.readUserState(userId), userId, ps);
}
if (mResolveComponentName.equals(component)) {
return PackageParser.generateActivityInfo(
@@ -5140,8 +5154,8 @@
ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) {
return false;
}
- for (int i=0; i<a.intents.size(); i++) {
- if (a.intents.get(i).match(intent.getAction(), resolvedType, intent.getScheme(),
+ for (int i=0; i< a.getIntents().size(); i++) {
+ if (a.getIntents().get(i).match(intent.getAction(), resolvedType, intent.getScheme(),
intent.getData(), intent.getCategories(), TAG) >= 0) {
return true;
}
@@ -5179,7 +5193,7 @@
return null;
}
return PackageInfoUtils.generateActivityInfo(pkg,
- a, flags, ps.readUserState(userId), userId);
+ a, flags, ps.readUserState(userId), userId, ps);
}
}
return null;
@@ -5400,7 +5414,7 @@
return null;
}
return PackageInfoUtils.generateServiceInfo(pkg,
- s, flags, ps.readUserState(userId), userId);
+ s, flags, ps.readUserState(userId), userId, ps);
}
}
return null;
@@ -5434,7 +5448,7 @@
return null;
}
PackageUserState state = ps.readUserState(userId);
- return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId);
+ return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId, ps);
}
}
return null;
@@ -8272,7 +8286,7 @@
continue;
}
ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, effectiveFlags,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
}
@@ -8298,7 +8312,7 @@
continue;
}
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
if (ai != null) {
ai.packageName = resolveExternalPackageNameLPr(p);
list.add(ai);
@@ -8451,13 +8465,13 @@
final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
&& p.isDirectBootAware();
- if ((p.getFlags() & ApplicationInfo.FLAG_PERSISTENT) != 0
- && (!mSafeMode || isSystemApp(p))
+ if (p.isPersistent()
+ && (!mSafeMode || p.isSystem())
&& (matchesUnaware || matchesAware)) {
PackageSetting ps = mSettings.mPackages.get(p.getPackageName());
if (ps != null) {
ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
- ps.readUserState(userId), userId);
+ ps.readUserState(userId), userId, ps);
if (ai != null) {
finalList.add(ai);
}
@@ -8563,7 +8577,7 @@
return null;
}
final ParsedInstrumentation i = mInstrumentation.get(component);
- return PackageInfoUtils.generateInstrumentationInfo(i, pkg, flags);
+ return PackageInfoUtils.generateInstrumentationInfo(i, pkg, flags, callingUserId, ps);
}
}
@@ -8576,11 +8590,12 @@
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return ParceledListSlice.emptyList();
}
- return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags));
+ return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags,
+ callingUserId));
}
private @NonNull List<InstrumentationInfo> queryInstrumentationInternal(String targetPackage,
- int flags) {
+ int flags, int userId) {
ArrayList<InstrumentationInfo> finalList = new ArrayList<>();
// reader
@@ -8590,10 +8605,12 @@
final ParsedInstrumentation p = i.next();
if (targetPackage == null
|| targetPackage.equals(p.getTargetPackage())) {
- AndroidPackage pkg = mPackages.get(p.getPackageName());
+ String packageName = p.getPackageName();
+ AndroidPackage pkg = mPackages.get(packageName);
+ PackageSetting pkgSetting = getPackageSetting(packageName);
if (pkg != null) {
InstrumentationInfo ii = PackageInfoUtils.generateInstrumentationInfo(p,
- pkg, flags);
+ pkg, flags, userId, pkgSetting);
if (ii != null) {
finalList.add(ii);
}
@@ -8606,7 +8623,7 @@
}
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
- long currentTime, PackageParser packageParser, ExecutorService executorService) {
+ long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
@@ -8616,7 +8633,7 @@
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
- PackageParser packageParser, ExecutorService executorService) {
+ PackageParser2 packageParser, ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + scanDir);
@@ -8720,7 +8737,8 @@
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
- ApkParseUtils.collectCertificates(parsedPackage, skipVerify);
+ parsedPackage.setSigningDetails(
+ ParsingPackageUtils.collectCertificates(parsedPackage, skipVerify));
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
@@ -8774,16 +8792,13 @@
private AndroidPackage scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
- PackageParser pp = new PackageParser();
- pp.setSeparateProcesses(mSeparateProcesses);
- pp.setOnlyCoreApps(mOnlyCore);
- pp.setDisplayMetrics(mMetrics);
- pp.setCallback(mPackageParserCallback);
+ PackageParser2 pp = new PackageParser2(mSeparateProcesses, mOnlyCore, mMetrics, null,
+ mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
try {
- parsedPackage = pp.parseParsedPackage(scanFile, parseFlags, false);
+ parsedPackage = pp.parsePackage(scanFile, parseFlags, false);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
@@ -8869,18 +8884,6 @@
final boolean pkgAlreadyExists;
PackageSetting pkgSetting;
- // NOTE: installPackageLI() has the same code to setup the package's
- // application info. This probably should be done lower in the call
- // stack [such as scanPackageOnly()]. However, we verify the application
- // info prior to that [in scanPackageNew()] and thus have to setup
- // the application info early.
- // TODO(b/135203078): Remove all of these application info calls
- parsedPackage.setApplicationVolumeUuid(parsedPackage.getVolumeUuid())
- .setApplicationInfoCodePath(parsedPackage.getCodePath())
- .setApplicationInfoResourcePath(parsedPackage.getCodePath())
- .setApplicationInfoBaseResourcePath(parsedPackage.getBaseCodePath())
- .setApplicationInfoSplitResourcePaths(parsedPackage.getSplitCodePaths());
-
synchronized (mLock) {
renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());
final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);
@@ -8933,8 +8936,8 @@
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
null, disabledPkgSetting /* pkgSetting */,
null /* disabledPkgSetting */, null /* originalPkgSetting */,
- null, parseFlags, scanFlags, isPlatformPackage, user);
- applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage);
+ null, parseFlags, scanFlags, isPlatformPackage, user, null);
+ applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, true);
final ScanResult scanResult =
scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L);
if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) {
@@ -9068,7 +9071,7 @@
}
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags, scanFlags
- | SCAN_UPDATE_SIGNATURE, currentTime, user);
+ | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
if (scanResult.success) {
synchronized (mLock) {
boolean appIdCreated = false;
@@ -9208,9 +9211,15 @@
return;
}
- List<AndroidPackage> pkgs;
+ List<PackageSetting> pkgSettings;
synchronized (mLock) {
- pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
+ pkgSettings = PackageManagerServiceUtils.getPackagesForDexopt(
+ mSettings.mPackages.values(), this);
+ }
+
+ List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
+ for (int index = 0; index < pkgSettings.size(); index++) {
+ pkgs.add(pkgSettings.get(index).pkg);
}
final long startTime = System.nanoTime();
@@ -9255,7 +9264,7 @@
boolean useProfileForDexopt = false;
- if ((isFirstBoot() || isDeviceUpgrading()) && isSystemApp(pkg)) {
+ if ((isFirstBoot() || isDeviceUpgrading()) && pkg.isSystem()) {
// Copy over initial preopt profiles since we won't get any JIT samples for methods
// that are already compiled.
File profileFile = new File(getPrebuildProfilePath(pkg));
@@ -9409,16 +9418,16 @@
@GuardedBy("mLock")
private void notifyPackageUseLocked(String packageName, int reason) {
- final AndroidPackage p = mPackages.get(packageName);
- if (p == null) {
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ if (pkgSetting == null) {
return;
}
- p.mutate().setLastPackageUsageTimeInMills(reason, System.currentTimeMillis());
+ pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, System.currentTimeMillis());
}
@Override
- public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
- List<String> classPaths, String loaderIsa) {
+ public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
+ String loaderIsa) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -9426,7 +9435,7 @@
+ loadingPackageName + ", user=" + userId);
return;
}
- mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
+ mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
}
@Override
@@ -9541,19 +9550,21 @@
// if the package can now be considered up to date for the given filter.
private int performDexOptInternal(DexoptOptions options) {
AndroidPackage p;
+ PackageSetting pkgSetting;
synchronized (mLock) {
p = mPackages.get(options.getPackageName());
- if (p == null) {
+ pkgSetting = mSettings.getPackageLPr(options.getPackageName());
+ if (p == null || pkgSetting == null) {
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
- mPackageUsage.maybeWriteAsync(mPackages);
+ mPackageUsage.maybeWriteAsync(mSettings.mPackages);
mCompilerStats.maybeWriteAsync();
}
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
- return performDexOptInternalWithDependenciesLI(p, options);
+ return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -9573,7 +9584,7 @@
}
private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
- DexoptOptions options) {
+ @NonNull PackageSetting pkgSetting, DexoptOptions options) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
@@ -9588,9 +9599,10 @@
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
// and the first package that uses the library will dexopt it. The
// others will see that the compiled code for the library is up to date.
- Collection<SharedLibraryInfo> deps = findSharedLibraries(p);
- final String[] instructionSets = getAppDexInstructionSets(p.getPrimaryCpuAbi(),
- p.getSecondaryCpuAbi());
+ Collection<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
+ final String[] instructionSets = getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
if (!deps.isEmpty()) {
DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
options.getCompilationReason(), options.getCompilerFilter(),
@@ -9598,12 +9610,14 @@
options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (SharedLibraryInfo info : deps) {
AndroidPackage depPackage = null;
+ PackageSetting depPackageSetting = null;
synchronized (mLock) {
depPackage = mPackages.get(info.getPackageName());
+ depPackageSetting = mSettings.getPackageLPr(info.getPackageName());
}
- if (depPackage != null) {
+ if (depPackage != null && depPackageSetting != null) {
// TODO: Analyze and investigate if we (should) profile libraries.
- pdo.performDexOpt(depPackage, instructionSets,
+ pdo.performDexOpt(depPackage, depPackageSetting, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
mDexManager.getPackageUseInfoOrDefault(depPackage.getPackageName()),
libraryOptions);
@@ -9612,7 +9626,8 @@
}
}
}
- return pdo.performDexOpt(p, instructionSets,
+
+ return pdo.performDexOpt(p, pkgSetting, instructionSets,
getOrCreateCompilerPackageStats(p),
mDexManager.getPackageUseInfoOrDefault(p.getPackageName()), options);
}
@@ -9655,11 +9670,11 @@
}
}
- private static List<SharedLibraryInfo> findSharedLibraries(AndroidPackage p) {
- if (p.getUsesLibraryInfos() != null) {
+ private static List<SharedLibraryInfo> findSharedLibraries(PackageSetting pkgSetting) {
+ if (!pkgSetting.getPkgState().getUsesLibraryInfos().isEmpty()) {
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
Set<String> collectedNames = new HashSet<>();
- for (SharedLibraryInfo info : p.getUsesLibraryInfos()) {
+ for (SharedLibraryInfo info : pkgSetting.getPkgState().getUsesLibraryInfos()) {
findSharedLibrariesRecursive(info, retValue, collectedNames);
}
return retValue;
@@ -9682,15 +9697,16 @@
}
}
- List<AndroidPackage> findSharedNonSystemLibraries(AndroidPackage pkg) {
- List<SharedLibraryInfo> deps = findSharedLibraries(pkg);
+ List<PackageSetting> findSharedNonSystemLibraries(PackageSetting pkgSetting) {
+ List<SharedLibraryInfo> deps = findSharedLibraries(pkgSetting);
if (!deps.isEmpty()) {
- ArrayList<AndroidPackage> retValue = new ArrayList<>();
+ List<PackageSetting> retValue = new ArrayList<>();
synchronized (mLock) {
for (SharedLibraryInfo info : deps) {
- AndroidPackage depPackage = mPackages.get(info.getPackageName());
- if (depPackage != null) {
- retValue.add(depPackage);
+ PackageSetting depPackageSetting =
+ mSettings.getPackageLPr(info.getPackageName());
+ if (depPackageSetting != null && depPackageSetting.pkg != null) {
+ retValue.add(depPackageSetting);
}
}
}
@@ -9762,7 +9778,7 @@
}
public void shutdown() {
- mPackageUsage.writeNow(mPackages);
+ mPackageUsage.writeNow(mSettings.mPackages);
mCompilerStats.writeNow();
mDexManager.writePackageDexUsageNow();
PackageWatchdog.getInstance(mContext).writeNow();
@@ -9808,9 +9824,11 @@
enforceSystemOrRoot("forceDexOpt");
AndroidPackage pkg;
+ PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
- if (pkg == null) {
+ pkgSetting = mSettings.getPackageLPr(packageName);
+ if (pkg == null || pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
}
@@ -9820,8 +9838,7 @@
// Whoever is calling forceDexOpt wants a compiled package.
// Don't use profiles since that may cause compilation to be skipped.
- final int res = performDexOptInternalWithDependenciesLI(
- pkg,
+ final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
new DexoptOptions(packageName,
getDefaultCompilerFilter(),
DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
@@ -9957,7 +9974,7 @@
// Or:
// - Package manager is in a state where package isn't scanned yet. This will
// get called again after scanning to fix the dependencies.
- if (pkg.isLibrary()) {
+ if (AndroidPackageUtils.isLibrary(pkg)) {
if (pkg.getStaticSharedLibName() != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfoLPr(
pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
@@ -9978,40 +9995,44 @@
@GuardedBy("mLock")
private void addSharedLibraryLPr(AndroidPackage pkg, Set<String> usesLibraryFiles,
- SharedLibraryInfo libInfo, AndroidPackage changingLib) {
+ SharedLibraryInfo libInfo, @Nullable AndroidPackage changingLib,
+ @Nullable PackageSetting changingLibSetting) {
if (libInfo.getPath() != null) {
usesLibraryFiles.add(libInfo.getPath());
return;
}
- AndroidPackage p = mPackages.get(libInfo.getPackageName());
+ AndroidPackage pkgForCodePaths = mPackages.get(libInfo.getPackageName());
+ PackageSetting pkgSetting = mSettings.getPackageLPr(libInfo.getPackageName());
if (changingLib != null && changingLib.getPackageName().equals(libInfo.getPackageName())) {
// If we are doing this while in the middle of updating a library apk,
// then we need to make sure to use that new apk for determining the
// dependencies here. (We haven't yet finished committing the new apk
// to the package manager state.)
- if (p == null || p.getPackageName().equals(changingLib.getPackageName())) {
- p = changingLib;
+ if (pkgForCodePaths == null
+ || pkgForCodePaths.getPackageName().equals(changingLib.getPackageName())) {
+ pkgForCodePaths = changingLib;
+ pkgSetting = changingLibSetting;
}
}
- if (p != null) {
- usesLibraryFiles.addAll(p.getAllCodePaths());
+ if (pkgForCodePaths != null) {
+ usesLibraryFiles.addAll(AndroidPackageUtils.getAllCodePaths(pkgForCodePaths));
// If the package provides libraries, add the dependency to them.
- applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, (definingLibrary, dependency) -> {
- definingLibrary.addDependency(dependency);
- });
- if (p.getUsesLibraryFiles() != null) {
- Collections.addAll(usesLibraryFiles, p.getUsesLibraryFiles());
+ applyDefiningSharedLibraryUpdateLocked(pkg, libInfo, SharedLibraryInfo::addDependency);
+ if (pkgSetting != null) {
+ usesLibraryFiles.addAll(pkgSetting.getPkgState().getUsesLibraryFiles());
}
}
}
@GuardedBy("mLock")
- private void updateSharedLibrariesLocked(AndroidPackage pkg,
- AndroidPackage changingLib, Map<String, AndroidPackage> availablePackages)
- throws PackageManagerException {
- final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
- collectSharedLibraryInfos(pkg, availablePackages, mSharedLibraries, null);
- executeSharedLibrariesUpdateLPr(pkg, changingLib, sharedLibraryInfos);
+ private void updateSharedLibrariesLocked(AndroidPackage pkg, PackageSetting pkgSetting,
+ @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting,
+ Map<String, AndroidPackage> availablePackages)
+ throws PackageManagerException {
+ final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
+ pkgSetting.pkg, availablePackages, mSharedLibraries, null);
+ executeSharedLibrariesUpdateLPr(pkg, pkgSetting, changingLib, changingLibSetting,
+ sharedLibraryInfos);
}
private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
@@ -10026,18 +10047,18 @@
// that libraries are searched in the correct order) and must have no
// duplicates.
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
- if (pkg.getUsesLibraries() != null) {
+ if (!pkg.getUsesLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
availablePackages, existingLibraries, newLibraries);
}
- if (pkg.getUsesStaticLibraries() != null) {
+ if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
availablePackages, existingLibraries, newLibraries);
}
- if (pkg.getUsesOptionalLibraries() != null) {
+ if (!pkg.getUsesOptionalLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
@@ -10046,25 +10067,27 @@
}
private void executeSharedLibrariesUpdateLPr(AndroidPackage pkg,
- AndroidPackage changingLib, ArrayList<SharedLibraryInfo> usesLibraryInfos) {
+ @NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib,
+ @Nullable PackageSetting changingLibSetting,
+ ArrayList<SharedLibraryInfo> usesLibraryInfos) {
// If the package provides libraries, clear their old dependencies.
// This method will set them up again.
applyDefiningSharedLibraryUpdateLocked(pkg, null, (definingLibrary, dependency) -> {
definingLibrary.clearDependencies();
});
if (usesLibraryInfos != null) {
- pkg.mutate().setUsesLibraryInfos(usesLibraryInfos);
+ pkgSetting.getPkgState().setUsesLibraryInfos(usesLibraryInfos);
// Use LinkedHashSet to preserve the order of files added to
// usesLibraryFiles while eliminating duplicates.
Set<String> usesLibraryFiles = new LinkedHashSet<>();
for (SharedLibraryInfo libInfo : usesLibraryInfos) {
- addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib);
+ addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib,
+ changingLibSetting);
}
- pkg.mutate().setUsesLibraryFiles(usesLibraryFiles.toArray(
- new String[usesLibraryFiles.size()]));
+ pkgSetting.getPkgState().setUsesLibraryFiles(new ArrayList<>(usesLibraryFiles));
} else {
- pkg.mutate().setUsesLibraryInfos(null)
- .setUsesLibraryFiles(null);
+ pkgSetting.getPkgState().setUsesLibraryInfos(Collections.emptyList())
+ .setUsesLibraryFiles(Collections.emptyList());
}
}
@@ -10180,27 +10203,32 @@
@GuardedBy("mLock")
private ArrayList<AndroidPackage> updateAllSharedLibrariesLocked(
- AndroidPackage updatedPkg,
+ @Nullable AndroidPackage updatedPkg, @Nullable PackageSetting updatedPkgSetting,
Map<String, AndroidPackage> availablePackages) {
ArrayList<AndroidPackage> resultList = null;
// Set of all descendants of a library; used to eliminate cycles
ArraySet<String> descendants = null;
// The current list of packages that need updating
- ArrayList<AndroidPackage> needsUpdating = null;
- if (updatedPkg != null) {
+ List<Pair<AndroidPackage, PackageSetting>> needsUpdating = null;
+ if (updatedPkg != null && updatedPkgSetting != null) {
needsUpdating = new ArrayList<>(1);
- needsUpdating.add(updatedPkg);
+ needsUpdating.add(Pair.create(updatedPkg, updatedPkgSetting));
}
do {
- final AndroidPackage changingPkg =
+ final Pair<AndroidPackage, PackageSetting> changingPkgPair =
(needsUpdating == null) ? null : needsUpdating.remove(0);
+ final AndroidPackage changingPkg = changingPkgPair != null
+ ? changingPkgPair.first : null;
+ final PackageSetting changingPkgSetting = changingPkgPair != null
+ ? changingPkgPair.second : null;
for (int i = mPackages.size() - 1; i >= 0; --i) {
final AndroidPackage pkg = mPackages.valueAt(i);
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
if (changingPkg != null
&& !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
&& !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
&& !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
- changingPkg.getStaticSharedLibName())) {
+ changingPkg.getStaticSharedLibName())) {
continue;
}
if (resultList == null) {
@@ -10214,19 +10242,20 @@
}
if (!descendants.contains(pkg.getPackageName())) {
descendants.add(pkg.getPackageName());
- needsUpdating.add(pkg);
+ needsUpdating.add(Pair.create(pkg, pkgSetting));
}
}
try {
- updateSharedLibrariesLocked(pkg, changingPkg, availablePackages);
+ updateSharedLibrariesLocked(pkg, pkgSetting, changingPkg,
+ changingPkgSetting, availablePackages);
} catch (PackageManagerException e) {
// If a system app update or an app and a required lib missing we
// delete the package and for updated system apps keep the data as
// it is better for the user to reinstall than to be in an limbo
// state. Also libs disappearing under an app should never happen
// - just in case.
- if (!pkg.isSystem() || pkg.isUpdatedSystemApp()) {
- final int flags = pkg.isUpdatedSystemApp()
+ if (!pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
+ final int flags = pkgSetting.getPkgState().isUpdatedSystemApp()
? PackageManager.DELETE_KEEP_DATA : 0;
deletePackageLIF(pkg.getPackageName(), null, true,
mUserManager.getUserIds(), flags, null,
@@ -10242,10 +10271,11 @@
@GuardedBy({"mInstallLock", "mLock"})
private ScanResult scanPackageTracedLI(ParsedPackage parsedPackage,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user) throws PackageManagerException {
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
- return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
+ return scanPackageNewLI(parsedPackage, parseFlags, scanFlags, currentTime, user,
+ cpuAbiOverride);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -10320,6 +10350,9 @@
@Nullable public final UserHandle user;
/** Whether or not the platform package is being scanned */
public final boolean isPlatformPackage;
+ /** Override value for package ABI if set during install */
+ @Nullable
+ public final String cpuAbiOverride;
public ScanRequest(
@NonNull ParsedPackage parsedPackage,
@Nullable SharedUserSetting sharedUserSetting,
@@ -10331,7 +10364,8 @@
@ParseFlags int parseFlags,
@ScanFlags int scanFlags,
boolean isPlatformPackage,
- @Nullable UserHandle user) {
+ @Nullable UserHandle user,
+ @Nullable String cpuAbiOverride) {
this.parsedPackage = parsedPackage;
this.oldPkg = oldPkg;
this.pkgSetting = pkgSetting;
@@ -10344,6 +10378,7 @@
this.scanFlags = scanFlags;
this.isPlatformPackage = isPlatformPackage;
this.user = user;
+ this.cpuAbiOverride = cpuAbiOverride;
}
}
@@ -10451,7 +10486,7 @@
@GuardedBy({"mInstallLock", "mLock"})
private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,
final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
- @Nullable UserHandle user) throws PackageManagerException {
+ @Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {
final String renamedPkgName = mSettings.getRenamedPackageLPr(
parsedPackage.getRealPackage());
@@ -10472,7 +10507,13 @@
scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);
synchronized (mLock) {
- applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage);
+ boolean isUpdatedSystemApp;
+ if (pkgSetting != null) {
+ isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+ } else {
+ isUpdatedSystemApp = disabledPkgSetting != null;
+ }
+ applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);
assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
SharedUserSetting sharedUserSetting = null;
@@ -10492,7 +10533,8 @@
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
- Objects.equals(parsedPackage.getPackageName(), platformPackageName), user);
+ Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
+ cpuAbiOverride);
return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);
}
}
@@ -10594,7 +10636,8 @@
}
if (reconciledPkg.collectedSharedLibraryInfos != null) {
- executeSharedLibrariesUpdateLPr(pkg, null, reconciledPkg.collectedSharedLibraryInfos);
+ executeSharedLibrariesUpdateLPr(pkg, pkgSetting, null, null,
+ reconciledPkg.collectedSharedLibraryInfos);
}
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -10607,7 +10650,7 @@
}
pkgSetting.signatures.mSigningDetails = reconciledPkg.signingDetails;
- if (pkg.getAdoptPermissions() != null) {
+ if (!pkg.getAdoptPermissions().isEmpty()) {
// This package wants to adopt ownership of permissions from
// another package.
for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) {
@@ -10661,8 +10704,7 @@
/** Returns {@code true} if the package has been renamed. Otherwise, {@code false}. */
private static boolean isPackageRenamed(@NonNull AndroidPackage pkg,
@Nullable String renamedPkgName) {
- return pkg.getOriginalPackages() != null
- && pkg.getOriginalPackages().contains(renamedPkgName);
+ return pkg.getOriginalPackages().contains(renamedPkgName);
}
/**
@@ -10714,8 +10756,7 @@
*/
private static void ensurePackageRenamed(@NonNull ParsedPackage parsedPackage,
@NonNull String renamedPackageName) {
- if (parsedPackage.getOriginalPackages() == null
- || !parsedPackage.getOriginalPackages().contains(renamedPackageName)
+ if (!parsedPackage.getOriginalPackages().contains(renamedPackageName)
|| parsedPackage.getPackageName().equals(renamedPackageName)) {
return;
}
@@ -10744,19 +10785,21 @@
}
ps.primaryCpuAbiString = adjustedAbi;
- if (ps.pkg != null && !TextUtils.equals(adjustedAbi, ps.pkg.getPrimaryCpuAbi())) {
- ps.pkg.mutate().setPrimaryCpuAbi(adjustedAbi);
- if (DEBUG_ABI_SELECTION) {
- Slog.i(TAG,
- "Adjusting ABI for " + ps.name + " to " + adjustedAbi
- + " (scannedPackage="
- + (scannedPackage != null ? scannedPackage : "null")
- + ")");
+ if (ps.pkg != null) {
+ if (!TextUtils.equals(adjustedAbi,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
+ if (DEBUG_ABI_SELECTION) {
+ Slog.i(TAG,
+ "Adjusting ABI for " + ps.name + " to " + adjustedAbi
+ + " (scannedPackage="
+ + (scannedPackage != null ? scannedPackage : "null")
+ + ")");
+ }
+ if (changedAbiCodePath == null) {
+ changedAbiCodePath = new ArrayList<>();
+ }
+ changedAbiCodePath.add(ps.codePathString);
}
- if (changedAbiCodePath == null) {
- changedAbiCodePath = new ArrayList<>();
- }
- changedAbiCodePath.add(ps.codePathString);
}
}
}
@@ -10766,6 +10809,8 @@
/**
* Sets the enabled state of components configured through {@link SystemConfig}.
* This modifies the {@link PackageSetting} object.
+ *
+ * TODO(b/135203078): Move this to package parsing
**/
static void configurePackageComponents(AndroidPackage pkg) {
final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
@@ -10776,7 +10821,7 @@
for (int i = ArrayUtils.size(pkg.getActivities()) - 1; i >= 0; i--) {
final ParsedActivity component = pkg.getActivities().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.className);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
@@ -10784,7 +10829,7 @@
for (int i = ArrayUtils.size(pkg.getReceivers()) - 1; i >= 0; i--) {
final ParsedActivity component = pkg.getReceivers().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.className);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
@@ -10792,7 +10837,7 @@
for (int i = ArrayUtils.size(pkg.getProviders()) - 1; i >= 0; i--) {
final ParsedProvider component = pkg.getProviders().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.className);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
@@ -10800,7 +10845,7 @@
for (int i = ArrayUtils.size(pkg.getServices()) - 1; i >= 0; i--) {
final ParsedService component = pkg.getServices().get(i);
- final Boolean enabled = componentsEnabledStates.get(component.className);
+ final Boolean enabled = componentsEnabledStates.get(component.getName());
if (enabled != null) {
component.setEnabled(enabled);
}
@@ -10848,8 +10893,8 @@
}
// Initialize package source and resource directories
- final File destCodeFile = new File(parsedPackage.getAppInfoCodePath());
- final File destResourceFile = new File(parsedPackage.getAppInfoResourcePath());
+ final File destCodeFile = new File(parsedPackage.getCodePath());
+ final File destResourceFile = new File(parsedPackage.getCodePath());
// We keep references to the derived CPU Abis from settings in oder to reuse
// them in the case where we're not upgrading or booting for the first time.
@@ -10878,10 +10923,13 @@
}
String[] usesStaticLibraries = null;
- if (parsedPackage.getUsesStaticLibraries() != null) {
+ if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);
}
+ // TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans
+ // to avoid adding something that's unsupported due to lack of state, since it's called
+ // with null.
final boolean createNewPackage = (pkgSetting == null);
if (createNewPackage) {
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
@@ -10890,16 +10938,18 @@
pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
- parsedPackage.getPrimaryCpuAbi(), parsedPackage.getSecondaryCpuAbi(),
- parsedPackage.getVersionCode(), parsedPackage.getFlags(),
- parsedPackage.getPrivateFlags(), user, true /*allowInstall*/, instantApp,
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ parsedPackage.getVersionCode(),
+ PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage),
+ PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage),
+ user, true /*allowInstall*/, instantApp,
virtualPreload, UserManagerService.getInstance(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups());
} else {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
- // TODO(b/135203078): Remove entirely. Set package directly.
- parsedPackage.setPackageSettingCallback(pkgSetting);
+ pkgSetting.pkg = parsedPackage;
// REMOVE SharedUserSetting from method; update in a separate call.
//
@@ -10908,8 +10958,10 @@
// to null here, only to reset them at a later point.
Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
- parsedPackage.getPrimaryCpuAbi(), parsedPackage.getSecondaryCpuAbi(),
- parsedPackage.getFlags(), parsedPackage.getPrivateFlags(),
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
+ PackageInfoWithoutStateUtils.appInfoFlags(parsedPackage),
+ PackageInfoWithoutStateUtils.appInfoPrivateFlags(parsedPackage),
UserManagerService.getInstance(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups());
@@ -10938,35 +10990,28 @@
if (disabledPkgSetting != null
|| (0 != (scanFlags & SCAN_NEW_INSTALL)
&& pkgSetting != null && pkgSetting.isSystem())) {
- parsedPackage.mutate().setUpdatedSystemApp(true);
+ pkgSetting.getPkgState().setUpdatedSystemApp(true);
}
parsedPackage
.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,
injector.getCompatibility()))
.setSeInfoUser(SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
- userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId)))
- .setProcessName(fixProcessName(parsedPackage.getPackageName(),
- parsedPackage.getProcessName()));
-
- if (!isPlatformPackage) {
- // Get all of our default paths setup
- parsedPackage.initForUser(UserHandle.USER_SYSTEM);
- }
+ userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId)));
if (parsedPackage.isSystem()) {
configurePackageComponents(parsedPackage);
}
- final String cpuAbiOverride = deriveAbiOverride(parsedPackage.getCpuAbiOverride(),
- pkgSetting);
+ final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
if (needToDeriveAbi) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
- final boolean extractNativeLibs = !parsedPackage.isLibrary();
+ final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
- packageAbiHelper.derivePackageAbi(parsedPackage, cpuAbiOverride,
+ packageAbiHelper.derivePackageAbi(parsedPackage,
+ pkgSetting.getPkgState().isUpdatedSystemApp(), cpuAbiOverride,
extractNativeLibs);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
@@ -10975,14 +11020,16 @@
// Some system apps still use directory structure for native libraries
// in which case we might end up not detecting abi solely based on apk
// structure. Try to detect abi based on directory structure.
- if (isSystemApp(parsedPackage) && !parsedPackage.isUpdatedSystemApp() &&
- parsedPackage.getPrimaryCpuAbi() == null) {
+
+ String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ if (parsedPackage.isSystem() && !pkgSetting.getPkgState().isUpdatedSystemApp() &&
+ pkgRawPrimaryCpuAbi == null) {
final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
parsedPackage);
abis.applyTo(parsedPackage);
abis.applyTo(pkgSetting);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage,
+ packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
@@ -10994,13 +11041,16 @@
.setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage, sAppLib32InstallDir);
+ packageAbiHelper.getNativeLibraryPaths(parsedPackage,
+ pkgSetting, sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
- parsedPackage.getPackageName() + " " + parsedPackage.getPrimaryCpuAbi()
- + ", " + parsedPackage.getSecondaryCpuAbi());
+ parsedPackage.getPackageName() + " " +
+ AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)
+ + ", "
+ + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
}
}
} else {
@@ -11017,7 +11067,8 @@
// ABIs we determined during compilation, but the path will depend on the final
// package path (after the rename away from the stage path).
final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
- packageAbiHelper.getNativeLibraryPaths(parsedPackage, sAppLib32InstallDir);
+ packageAbiHelper.getNativeLibraryPaths(parsedPackage, pkgSetting,
+ sAppLib32InstallDir);
nativeLibraryPaths.applyTo(parsedPackage);
}
@@ -11035,20 +11086,16 @@
// would've already compiled the app without taking the package setting into
// account.
if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
- if (cpuAbiOverride == null && parsedPackage.getPackageName() != null) {
+ if (cpuAbiOverride == null) {
Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
" for package " + parsedPackage.getPackageName());
}
}
- pkgSetting.primaryCpuAbiString = parsedPackage.getPrimaryCpuAbi();
- pkgSetting.secondaryCpuAbiString = parsedPackage.getSecondaryCpuAbi();
+ pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
+ pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
- // Copy the derived override back to the parsed package, so that we can
- // update the package settings accordingly.
- parsedPackage.setCpuAbiOverride(cpuAbiOverride);
-
if (DEBUG_ABI_SELECTION) {
Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
+ " to root=" + parsedPackage.getNativeLibraryRootDir() + ", isa="
@@ -11061,8 +11108,8 @@
if (DEBUG_ABI_SELECTION) {
Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are" +
- " primary=" + parsedPackage.getPrimaryCpuAbi() +
- " secondary=" + parsedPackage.getSecondaryCpuAbi());
+ " primary=" + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage) +
+ " secondary=" + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
}
if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
@@ -11104,13 +11151,13 @@
}
pkgSetting.setTimeStamp(scanFileTime);
// TODO(b/135203078): Remove, move to constructor
- parsedPackage.setPackageSettingCallback(pkgSetting);
- pkgSetting.pkgFlags = parsedPackage.getFlags();
+ pkgSetting.pkg = parsedPackage;
+ pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
pkgSetting.versionCode = parsedPackage.getLongVersionCode();
}
// Update volume if needed
- final String volumeUuid = parsedPackage.getApplicationInfoVolumeUuid();
+ final String volumeUuid = parsedPackage.getVolumeUuid();
if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
Slog.i(PackageManagerService.TAG,
"Update" + (pkgSetting.isSystem() ? " system" : "")
@@ -11122,14 +11169,15 @@
SharedLibraryInfo staticSharedLibraryInfo = null;
if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
- staticSharedLibraryInfo = SharedLibraryInfo.createForStatic(parsedPackage);
+ staticSharedLibraryInfo =
+ AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
}
List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;
if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {
dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());
for (String name : parsedPackage.getLibraryNames()) {
dynamicSharedLibraryInfos.add(
- SharedLibraryInfo.createForDynamic(parsedPackage, name));
+ AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));
}
}
@@ -11167,7 +11215,7 @@
*/
private static void assertCodePolicy(AndroidPackage pkg)
throws PackageManagerException {
- final boolean shouldHaveCode = (pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ final boolean shouldHaveCode = pkg.isHasCode();
if (shouldHaveCode && !apkHasCode(pkg.getBaseCodePath())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Package " + pkg.getBaseCodePath() + " code is missing");
@@ -11193,7 +11241,8 @@
* ideally be static, but, it requires locks to read system state.
*/
private static void applyPolicy(ParsedPackage parsedPackage, final @ParseFlags int parseFlags,
- final @ScanFlags int scanFlags, AndroidPackage platformPkg) {
+ final @ScanFlags int scanFlags, AndroidPackage platformPkg,
+ boolean isUpdatedSystemApp) {
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
parsedPackage.setSystem(true);
// TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
@@ -11202,7 +11251,7 @@
parsedPackage.setAllComponentsDirectBootAware(true);
}
if (compressedFileExists(parsedPackage.getCodePath())) {
- parsedPackage.setIsStub(true);
+ parsedPackage.setStub(true);
}
} else {
parsedPackage
@@ -11237,14 +11286,14 @@
) == PackageManager.SIGNATURE_MATCH))
);
- if (!isSystemApp(parsedPackage)) {
+ if (!parsedPackage.isSystem()) {
// Only system apps can use these features.
parsedPackage.clearOriginalPackages()
.setRealPackage(null)
.clearAdoptPermissions();
}
- PackageBackwardCompatibility.modifySharedLibraries(parsedPackage);
+ PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
}
private static @NonNull <T> T assertNotNull(@Nullable T object, String message)
@@ -11255,19 +11304,19 @@
return object;
}
- private <T extends ComponentParseUtils.ParsedMainComponent>
+ private <T extends ParsedMainComponent>
void assertPackageProcesses(AndroidPackage pkg, List<T> components,
- ArrayMap<String, ComponentParseUtils.ParsedProcess> procs, String compName)
+ Map<String, ParsedProcess> procs, String compName)
throws PackageManagerException {
if (components == null) {
return;
}
for (int i = components.size() - 1; i >= 0; i--) {
- final ComponentParseUtils.ParsedMainComponent<?> component = components.get(i);
+ final ParsedMainComponent component = components.get(i);
if (!procs.containsKey(component.getProcessName())) {
throw new PackageManagerException(
INSTALL_FAILED_PROCESS_NOT_DEFINED,
- "Can't install because " + compName + " " + component.className
+ "Can't install because " + compName + " " + component.getClassName()
+ "'s process attribute " + component.getProcessName()
+ " (in package " + pkg.getPackageName()
+ ") is not included in the <processes> list");
@@ -11291,8 +11340,7 @@
assertCodePolicy(pkg);
}
- if (pkg.getAppInfoCodePath() == null ||
- pkg.getAppInfoResourcePath() == null) {
+ if (pkg.getCodePath() == null) {
// Bail out. The resource and code paths haven't been set.
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Code and resource paths haven't been set correctly");
@@ -11375,49 +11423,49 @@
}
// Static shared libs cannot declare activities
- if (pkg.getActivities() != null && !pkg.getActivities().isEmpty()) {
+ if (!pkg.getActivities().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare activities");
}
// Static shared libs cannot declare services
- if (pkg.getServices() != null && !pkg.getServices().isEmpty()) {
+ if (!pkg.getServices().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare services");
}
// Static shared libs cannot declare providers
- if (pkg.getProviders() != null && !pkg.getProviders().isEmpty()) {
+ if (!pkg.getProviders().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare content providers");
}
// Static shared libs cannot declare receivers
- if (pkg.getReceivers() != null && !pkg.getReceivers().isEmpty()) {
+ if (!pkg.getReceivers().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare broadcast receivers");
}
// Static shared libs cannot declare permission groups
- if (pkg.getPermissionGroups() != null && !pkg.getPermissionGroups().isEmpty()) {
+ if (!pkg.getPermissionGroups().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare permission groups");
}
// Static shared libs cannot declare features
- if (pkg.getFeatures() != null && !pkg.getFeatures().isEmpty()) {
+ if (!pkg.getFeatures().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare features");
}
// Static shared libs cannot declare permissions
- if (pkg.getPermissions() != null && !pkg.getPermissions().isEmpty()) {
+ if (!pkg.getPermissions().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare permissions");
}
// Static shared libs cannot declare protected broadcasts
- if (pkg.getProtectedBroadcasts() != null) {
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
throw new PackageManagerException(
"Static shared libs cannot declare protected broadcasts");
}
@@ -11479,12 +11527,11 @@
+ " and requiring known paths " + known.codePathString
+ " & " + known.resourcePathString);
}
- if (!pkg.getAppInfoCodePath().equals(known.codePathString)
- || !pkg.getAppInfoResourcePath().equals(
- known.resourcePathString)) {
+ if (!pkg.getCodePath().equals(known.codePathString)
+ || !pkg.getCodePath().equals(known.resourcePathString)) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
"Application package " + pkg.getPackageName()
- + " found at " + pkg.getAppInfoCodePath()
+ + " found at " + pkg.getCodePath()
+ " but expected at " + known.codePathString
+ "; ignoring.");
}
@@ -11506,8 +11553,8 @@
// If this package has defined explicit processes, then ensure that these are
// the only processes used by its components.
- final ArrayMap<String, ComponentParseUtils.ParsedProcess> procs = pkg.getProcesses();
- if (procs != null) {
+ final Map<String, ParsedProcess> procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
if (!procs.containsKey(pkg.getProcessName())) {
throw new PackageManagerException(
INSTALL_FAILED_PROCESS_NOT_DEFINED,
@@ -11761,14 +11808,16 @@
reconciledPkg.getCombinedAvailablePackages();
try {
// Shared libraries for the package need to be updated.
- updateSharedLibrariesLocked(pkg, null, combinedSigningDetails);
+ updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+ combinedSigningDetails);
} catch (PackageManagerException e) {
Slog.e(TAG, "updateSharedLibrariesLPr failed: ", e);
}
// Update all applications that use this library. Skip when booting
// since this will be done after all packages are scaned.
if ((scanFlags & SCAN_BOOTING) == 0) {
- clientLibPkgs = updateAllSharedLibrariesLocked(pkg, combinedSigningDetails);
+ clientLibPkgs = updateAllSharedLibrariesLocked(pkg, pkgSetting,
+ combinedSigningDetails);
}
}
}
@@ -11793,7 +11842,7 @@
if (clientLibPkgs != null) {
for (int i=0; i<clientLibPkgs.size(); i++) {
AndroidPackage clientPkg = clientLibPkgs.get(i);
- killApplication(clientPkg.getAppInfoPackageName(),
+ killApplication(clientPkg.getPackageName(),
clientPkg.getUid(), "update lib");
}
}
@@ -11807,7 +11856,7 @@
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
- mPackages.put(pkg.getAppInfoPackageName(), pkg);
+ mPackages.put(pkg.getPackageName(), pkg);
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
mApexManager.registerApkInApex(pkg);
}
@@ -11855,7 +11904,7 @@
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r);
}
- if (pkg.getProtectedBroadcasts() != null) {
+ if (!pkg.getProtectedBroadcasts().isEmpty()) {
synchronized (mProtectedBroadcasts) {
mProtectedBroadcasts.addAll(pkg.getProtectedBroadcasts());
}
@@ -11887,8 +11936,8 @@
// Set up information for custom user intent resolution activity.
mResolveActivity.applicationInfo = pkg.toAppInfoWithoutState();
mResolveActivity.name = mCustomResolverComponentName.getClassName();
- mResolveActivity.packageName = pkg.getAppInfoPackageName();
- mResolveActivity.processName = pkg.getAppInfoProcessName();
+ mResolveActivity.packageName = pkg.getPackageName();
+ mResolveActivity.processName = pkg.getProcessName();
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
@@ -12006,21 +12055,19 @@
}
r = null;
- if ((pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (pkg.isSystem()) {
// Only system apps can hold shared libraries.
- if (pkg.getLibraryNames() != null) {
- final int libraryNamesSize = pkg.getLibraryNames().size();
- for (i = 0; i < libraryNamesSize; i++) {
- String name = pkg.getLibraryNames().get(i);
- if (removeSharedLibraryLPw(name, 0)) {
- if (DEBUG_REMOVE && chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(name);
+ final int libraryNamesSize = pkg.getLibraryNames().size();
+ for (i = 0; i < libraryNamesSize; i++) {
+ String name = pkg.getLibraryNames().get(i);
+ if (removeSharedLibraryLPw(name, 0)) {
+ if (DEBUG_REMOVE && chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
}
+ r.append(name);
}
}
}
@@ -12431,18 +12478,12 @@
if (pkgSetting == null || !pkgSetting.isSystem()) {
return;
}
- AndroidPackage pkg = pkgSetting.pkg;
- if (pkg != null) {
- pkg.mutate().setHiddenUntilInstalled(hidden);
- }
+ pkgSetting.getPkgState().setHiddenUntilInstalled(hidden);
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
if (disabledPs == null) {
return;
}
- pkg = disabledPs.pkg;
- if (pkg != null) {
- pkg.mutate().setHiddenUntilInstalled(hidden);
- }
+ disabledPs.getPkgState().setHiddenUntilInstalled(hidden);
}
}
@@ -13464,15 +13505,15 @@
ArrayList<IntentFilter> result = new ArrayList<>();
for (int n=0; n<count; n++) {
ParsedActivity activity = pkg.getActivities().get(n);
- if (activity.intents != null && activity.intents.size() > 0) {
- result.addAll(activity.intents);
+ if (activity.getIntents() != null && activity.getIntents().size() > 0) {
+ result.addAll(activity.getIntents());
}
}
return new ParceledListSlice<IntentFilter>(result) {
@Override
protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
// IntentFilter has final Parcelable methods, so redirect to the subclass
- ((ParsedActivityIntentInfo) parcelable).writeIntentInfoToParcel(dest,
+ ((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
callFlags);
}
};
@@ -13657,9 +13698,8 @@
// package has not opted out of backup participation.
final boolean update = res.removedInfo != null
&& res.removedInfo.removedPackage != null;
- final int flags = (res.pkg == null) ? 0 : res.pkg.getFlags();
- boolean doRestore = !update
- && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
+ boolean allowBackup = res.pkg != null && res.pkg.isAllowBackup();
+ boolean doRestore = !update && allowBackup;
// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
@@ -13727,7 +13767,7 @@
try {
if (bm.isUserReadyForBackup(userId)) {
bm.restoreAtInstallForUser(
- userId, res.pkg.getAppInfoPackageName(), token);
+ userId, res.pkg.getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
+ "didn't take place.");
@@ -13755,8 +13795,7 @@
IRollbackManager rm = IRollbackManager.Stub.asInterface(
ServiceManager.getService(Context.ROLLBACK_SERVICE));
- final String packageName = res.pkg.getAppInfoPackageName();
- final String seInfo = res.pkg.getSeInfo();
+ final String packageName = res.pkg.getPackageName();
final int[] allUsers = mUserManager.getUserIds();
final int[] installedUsers;
@@ -13780,6 +13819,7 @@
|| (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
if (ps != null && doSnapshotOrRestore) {
+ final String seInfo = AndroidPackageUtils.getSeInfo(res.pkg, ps);
try {
rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
seInfo, token);
@@ -13815,7 +13855,7 @@
if (data.res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
continue;
}
- if (packageName.equals(data.res.pkg.getAppInfoPackageName())) {
+ if (packageName.equals(data.res.pkg.getPackageName())) {
// right package; but is it for the right user?
for (int uIndex = 0; uIndex < data.res.newUsers.length; uIndex++) {
if (userId == data.res.newUsers[uIndex]) {
@@ -13951,20 +13991,18 @@
final String fromUuid;
final String toUuid;
final String packageName;
- final String dataAppName;
final int appId;
final String seinfo;
final int targetSdkVersion;
final String fromCodePath;
public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName,
- String dataAppName, int appId, String seinfo, int targetSdkVersion,
+ int appId, String seinfo, int targetSdkVersion,
String fromCodePath) {
this.moveId = moveId;
this.fromUuid = fromUuid;
this.toUuid = toUuid;
this.packageName = packageName;
- this.dataAppName = dataAppName;
this.appId = appId;
this.seinfo = seinfo;
this.targetSdkVersion = targetSdkVersion;
@@ -14200,7 +14238,7 @@
if (dataOwnerPkg != null) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
- dataOwnerPkg.getFlags())) {
+ dataOwnerPkg.isDebuggable())) {
try {
checkDowngrade(dataOwnerPkg, pkgLite);
} catch (PackageManagerException e) {
@@ -14213,7 +14251,7 @@
if (installedPkg != null) {
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for updated system application.
- if ((installedPkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (installedPkg.isSystem()) {
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
} else {
// If current upgrade specifies particular preference
@@ -14224,7 +14262,7 @@
// App explictly prefers external. Let policy decide
} else {
// Prefer previous location
- if (isExternal(installedPkg)) {
+ if (installedPkg.isExternalStorage()) {
return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
}
return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
@@ -15000,14 +15038,6 @@
parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
afterCodeFile, parsedPackage.getSplitCodePaths()));
- // Reflect the rename in app info
- // TODO(b/135203078): Remove all of these application info calls
- parsedPackage.setApplicationVolumeUuid(parsedPackage.getVolumeUuid())
- .setApplicationInfoCodePath(parsedPackage.getCodePath())
- .setApplicationInfoResourcePath(parsedPackage.getCodePath())
- .setApplicationInfoBaseResourcePath(parsedPackage.getBaseCodePath())
- .setApplicationInfoSplitResourcePaths(parsedPackage.getSplitCodePaths());
-
return true;
}
@@ -15088,7 +15118,7 @@
synchronized (mInstaller) {
try {
mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName,
- move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion,
+ move.appId, move.seinfo, move.targetSdkVersion,
move.fromCodePath);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to move app", e);
@@ -15096,7 +15126,8 @@
}
}
- codeFile = new File(Environment.getDataAppDirectory(move.toUuid), move.dataAppName);
+ final String toPathName = new File(move.fromCodePath).getName();
+ codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName);
resourceFile = codeFile;
if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
@@ -15117,14 +15148,6 @@
return false;
}
- // Reflect the move in app info
- // TODO(b/135203078): Remove all of these application info calls
- parsedPackage.setApplicationVolumeUuid(parsedPackage.getVolumeUuid())
- .setApplicationInfoCodePath(parsedPackage.getCodePath())
- .setApplicationInfoResourcePath(parsedPackage.getCodePath())
- .setApplicationInfoBaseResourcePath(parsedPackage.getBaseCodePath())
- .setApplicationInfoSplitResourcePaths(parsedPackage.getSplitCodePaths());
-
return true;
}
@@ -15148,8 +15171,9 @@
}
private boolean cleanUp(String volumeUuid) {
+ final String toPathName = new File(move.fromCodePath).getName();
final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
- move.dataAppName);
+ toPathName);
Slog.d(TAG, "Cleaning up " + move.packageName + " on " + volumeUuid);
final int[] userIds = mUserManager.getUserIds();
synchronized (mInstallLock) {
@@ -15320,7 +15344,7 @@
final PackageSetting ps = mSettings.mPackages.get(pkgName);
final int userId = installArgs.user.getIdentifier();
if (ps != null) {
- if (isSystemApp(pkg)) {
+ if (pkg.isSystem()) {
if (DEBUG_INSTALL) {
Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
}
@@ -15349,8 +15373,8 @@
}
// Retrieve the overlays for shared libraries of the package.
- if (pkg.getUsesLibraryInfos() != null) {
- for (SharedLibraryInfo sharedLib : pkg.getUsesLibraryInfos()) {
+ if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
+ for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
if (!sharedLib.isDynamic()) {
// TODO(146804378): Support overlaying static shared libraries
@@ -15822,13 +15846,13 @@
if (scanResult.staticSharedLibraryInfo != null) {
return Collections.singletonList(scanResult.staticSharedLibraryInfo);
}
- final boolean hasDynamicLibraries =
- (parsedPackage.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
+ final boolean hasDynamicLibraries = parsedPackage.isSystem()
&& scanResult.dynamicSharedLibraryInfos != null;
if (!hasDynamicLibraries) {
return null;
}
- final boolean isUpdatedSystemApp = parsedPackage.isUpdatedSystemApp();
+ final boolean isUpdatedSystemApp = scanResult.pkgSetting.getPkgState()
+ .isUpdatedSystemApp();
// We may not yet have disabled the updated package yet, so be sure to grab the
// current setting if that's the case.
final PackageSetting updatedSystemPs = isUpdatedSystemApp
@@ -15925,10 +15949,13 @@
// which means we are replacing another update that is already
// installed. We need to make sure to delete the older one's .apk.
res.removedInfo.args = createInstallArgsForExisting(
- oldPackage.getAppInfoCodePath(),
- oldPackage.getAppInfoResourcePath(),
- getAppDexInstructionSets(oldPackage.getPrimaryCpuAbi(),
- oldPackage.getSecondaryCpuAbi()));
+ oldPackage.getCodePath(),
+ oldPackage.getCodePath(),
+ getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
+ deletedPkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
+ deletedPkgSetting)));
} else {
res.removedInfo.args = null;
}
@@ -15947,14 +15974,14 @@
// If deleted package lived in a container, give users a chance to
// relinquish resources before killing.
- if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+ if (oldPackage.isExternalStorage()) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + oldPackage
+ " is ASEC-hosted -> UNAVAILABLE");
}
final int[] uidArray = new int[]{oldPackage.getUid()};
final ArrayList<String> pkgList = new ArrayList<>(1);
- pkgList.add(oldPackage.getAppInfoPackageName());
+ pkgList.add(oldPackage.getPackageName());
sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
}
@@ -16061,7 +16088,7 @@
final ScanResult result = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
- request.args.user);
+ request.args.user, request.args.abiOverride);
if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {
request.installResult.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
@@ -16194,7 +16221,7 @@
final boolean performDexopt =
(!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
- && ((pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) == 0)
+ && !pkg.isDebuggable()
&& (!onIncremental);
if (performDexopt) {
@@ -16214,7 +16241,7 @@
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE
| DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
- mPackageDexOptimizer.performDexOpt(pkg,
+ mPackageDexOptimizer.performDexOpt(pkg, reconciledPkg.pkgSetting,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(packageName),
@@ -16331,16 +16358,14 @@
| PackageParser.PARSE_ENFORCE_CODE
| (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
- PackageParser pp = new PackageParser();
- pp.setSeparateProcesses(mSeparateProcesses);
- pp.setDisplayMetrics(mMetrics);
- pp.setCallback(mPackageParserCallback);
+ PackageParser2 pp = new PackageParser2(mSeparateProcesses, false, mMetrics, null,
+ mPackageParserCallback);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
ParsedPackage parsedPackage;
try {
- parsedPackage = pp.parseParsedPackage(tmpPackageFile, parseFlags, false);
- DexMetadataHelper.validatePackageDexMetadata(parsedPackage);
+ parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
+ AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
} catch (PackageParserException e) {
throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
@@ -16375,16 +16400,8 @@
}
}
- // If package doesn't declare API override, mark that we have an install
- // time CPU ABI override.
- // TODO(b/135203078): Isn't this always true because cpuAbiOverride isn't assigned during
- // parsing?
- if (TextUtils.isEmpty(parsedPackage.getCpuAbiOverride())) {
- parsedPackage.setCpuAbiOverride(args.abiOverride);
- }
-
String pkgName = res.name = parsedPackage.getPackageName();
- if ((parsedPackage.getFlags() & ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+ if (parsedPackage.isTestOnly()) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
}
@@ -16396,7 +16413,8 @@
parsedPackage.setSigningDetails(args.signingDetails);
} else {
// TODO(b/136132412): skip for Incremental installation
- ApkParseUtils.collectCertificates(parsedPackage, false /* skipVerify */);
+ parsedPackage.setSigningDetails(
+ ParsingPackageUtils.collectCertificates(parsedPackage, false /* skipVerify */));
}
} catch (PackageParserException e) {
throw new PrepareFailure("Failed collect during installPackageLI", e);
@@ -16418,8 +16436,7 @@
// Check if installing already existing package
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.getRenamedPackageLPr(pkgName);
- if (parsedPackage.getOriginalPackages() != null
- && parsedPackage.getOriginalPackages().contains(oldName)
+ if (parsedPackage.getOriginalPackages().contains(oldName)
&& mPackages.containsKey(oldName)) {
// This package is derived from an original package,
// and this device has been updating from that original
@@ -16454,7 +16471,7 @@
+ " target SDK " + oldTargetSdk + " does.");
}
// Prevent persistent apps from being updated
- if (((oldPackage.getFlags() & ApplicationInfo.FLAG_PERSISTENT) != 0)
+ if (oldPackage.isPersistent()
&& ((installFlags & PackageManager.INSTALL_STAGED) == 0)) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_INVALID_APK,
"Package " + oldPackage.getPackageName() + " is a persistent app. "
@@ -16509,7 +16526,7 @@
}
if (ps.pkg != null) {
- systemApp = (ps.pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
+ systemApp = ps.pkg.isSystem();
}
res.origUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
}
@@ -16521,12 +16538,12 @@
final BasePermission bp = mPermissionManager.getPermissionTEMP(perm.getName());
// Don't allow anyone but the system to define ephemeral permissions.
- if ((perm.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
+ if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0
&& !systemApp) {
Slog.w(TAG, "Non-System package " + parsedPackage.getPackageName()
+ " attempting to delcare ephemeral permission "
+ perm.getName() + "; Removing ephemeral.");
- perm.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
+ perm.setProtectionLevel(perm.getProtectionLevel() & ~PermissionInfo.PROTECTION_FLAG_INSTANT);
}
// Check whether the newly-scanned package wants to define an already-defined perm
@@ -16587,14 +16604,14 @@
// type as this would allow a privilege escalation where an app adds a
// normal/signature permission in other app's group and later redefines
// it as dangerous leading to the group auto-grant.
- if ((perm.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS) {
if (bp != null && !bp.isRuntime()) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " trying to change a non-runtime permission "
+ perm.getName()
+ " to runtime; keeping old protection level");
- perm.protectionLevel = bp.getProtectionLevel();
+ perm.setProtectionLevel(bp.getProtectionLevel());
}
}
}
@@ -16637,12 +16654,22 @@
scanFlags |= SCAN_NO_DEX;
try {
- String abiOverride = (TextUtils.isEmpty(parsedPackage.getCpuAbiOverride())
- ? args.abiOverride : parsedPackage.getCpuAbiOverride());
- final boolean extractNativeLibs = !parsedPackage.isLibrary();
+ final boolean extractNativeLibs = !AndroidPackageUtils.isLibrary(parsedPackage);
+ PackageSetting pkgSetting;
+ synchronized (mLock) {
+ pkgSetting = mSettings.getPackageLPr(pkgName);
+ }
+ String abiOverride =
+ (pkgSetting == null || TextUtils.isEmpty(pkgSetting.cpuAbiOverrideString)
+ ? args.abiOverride : pkgSetting.cpuAbiOverrideString);
+ boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
+ && pkgSetting.getPkgState().isUpdatedSystemApp();
+ AndroidPackage oldPackage = mPackages.get(pkgName);
+ boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
- derivedAbi = mInjector.getAbiHelper().derivePackageAbi(
- parsedPackage, abiOverride, extractNativeLibs);
+ derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
+ isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
+ abiOverride, extractNativeLibs);
derivedAbi.first.applyTo(parsedPackage);
derivedAbi.second.applyTo(parsedPackage);
} catch (PackageManagerException pme) {
@@ -16817,14 +16844,14 @@
res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
}
- sysPkg = (isSystemApp(oldPackage));
+ sysPkg = oldPackage.isSystem();
if (sysPkg) {
// Set the system/privileged/oem/vendor/product flags as needed
- final boolean privileged = isPrivilegedApp(oldPackage);
- final boolean oem = isOemApp(oldPackage);
- final boolean vendor = isVendorApp(oldPackage);
- final boolean product = isProductApp(oldPackage);
- final boolean odm = isOdmApp(oldPackage);
+ final boolean privileged = oldPackage.isPrivileged();
+ final boolean oem = oldPackage.isOem();
+ final boolean vendor = oldPackage.isVendor();
+ final boolean product = oldPackage.isProduct();
+ final boolean odm = oldPackage.isOdm();
final @ParseFlags int systemParseFlags = parseFlags;
final @ScanFlags int systemScanFlags = scanFlags
| SCAN_AS_SYSTEM
@@ -16839,7 +16866,7 @@
+ ", old=" + oldPackage);
}
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- parsedPackage.setUpdatedSystemApp(true);
+ ps.getPkgState().setUpdatedSystemApp(true);
targetParseFlags = systemParseFlags;
targetScanFlags = systemScanFlags;
} else { // non system replace
@@ -17006,7 +17033,7 @@
Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
msg.obj = new IFVerificationParams(
pkg.getPackageName(),
- hasDomainURLs(pkg),
+ pkg.isHasDomainUrls(),
pkg.getActivities(),
replacing,
userId,
@@ -17058,11 +17085,12 @@
// examine handling policy even if not re-verifying.
boolean needToVerify = false;
for (ParsedActivity a : activities) {
- for (ParsedActivityIntentInfo filter : a.intents) {
+ for (ParsedIntentInfo filter : a.getIntents()) {
if (filter.handlesWebUris(true)) {
handlesWebUris = true;
}
- if (filter.needsVerification() && needsNetworkVerificationLPr(filter)) {
+ if (filter.needsVerification()
+ && needsNetworkVerificationLPr(a.getPackageName())) {
if (DEBUG_DOMAIN_VERIFICATION) {
Slog.d(TAG,
"Intent filter needs verification, so processing all filters");
@@ -17082,8 +17110,9 @@
if (needToVerify) {
final int verificationId = mIntentFilterVerificationToken++;
for (ParsedActivity a : activities) {
- for (ParsedActivityIntentInfo filter : a.intents) {
- if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
+ for (ParsedIntentInfo filter : a.getIntents()) {
+ if (filter.handlesWebUris(true)
+ && needsNetworkVerificationLPr(a.getPackageName())) {
if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
"Verification needed for IntentFilter:" + filter.toString());
mIntentFilterVerifier.addOneIntentFilterVerification(
@@ -17118,9 +17147,7 @@
}
@GuardedBy("mLock")
- private boolean needsNetworkVerificationLPr(ParsedActivityIntentInfo filter) {
- final String packageName = filter.getPackageName();
-
+ private boolean needsNetworkVerificationLPr(String packageName) {
IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
packageName);
if (ivi == null) {
@@ -17139,47 +17166,10 @@
}
}
- private static boolean isExternal(AndroidPackage pkg) {
- return (pkg.getFlags() & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
- }
-
private static boolean isExternal(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
- static boolean isSystemApp(AndroidPackage pkg) {
- return (pkg.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0;
- }
-
- private static boolean isPrivilegedApp(AndroidPackage pkg) {
- return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
- }
-
- private static boolean isOemApp(AndroidPackage pkg) {
- return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
- }
-
- private static boolean isVendorApp(AndroidPackage pkg) {
- return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
- }
-
- private static boolean isProductApp(AndroidPackage pkg) {
- return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
- }
-
- private static boolean isSystemExtApp(AndroidPackage pkg) {
- return (pkg.getPrivateFlags()
- & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
- }
-
- private static boolean isOdmApp(AndroidPackage pkg) {
- return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
- }
-
- private static boolean hasDomainURLs(AndroidPackage pkg) {
- return (pkg.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
- }
-
private static boolean isSystemApp(PackageSetting ps) {
return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
@@ -17189,7 +17179,7 @@
}
private VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
- if (isExternal(pkg)) {
+ if (pkg.isExternalStorage()) {
if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
return mSettings.getExternalVersion();
} else {
@@ -17649,10 +17639,11 @@
final AndroidPackage stubPkg =
(disabledSystemPs == null) ? null : disabledSystemPs.pkg;
if (stubPkg != null && stubPkg.isStub()) {
+ final PackageSetting stubPs;
synchronized (mLock) {
// restore the enabled state of the stub; the state is overwritten when
// the stub is uninstalled
- final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.getPackageName());
+ stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
if (stubPs != null) {
stubPs.setEnabled(origEnabledState, userId, "android");
}
@@ -17663,7 +17654,7 @@
Slog.i(TAG, "Enabling system stub after removal; pkg: "
+ stubPkg.getPackageName());
}
- enableCompressedPackage(stubPkg);
+ enableCompressedPackage(stubPkg, stubPs);
}
}
}
@@ -18046,9 +18037,12 @@
final AndroidPackage pkg =
scanPackageTracedLI(codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
+ PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.getPackageName());
+
try {
// update shared libraries for the newly re-installed system package
- updateSharedLibrariesLocked(pkg, null, Collections.unmodifiableMap(mPackages));
+ updateSharedLibrariesLocked(pkg, pkgSetting, null, null,
+ Collections.unmodifiableMap(mPackages));
} catch (PackageManagerException e) {
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
@@ -18479,10 +18473,11 @@
// Try finding details about the requested package
AndroidPackage pkg;
+ PackageSetting ps;
synchronized (mLock) {
pkg = mPackages.get(packageName);
+ ps = mSettings.mPackages.get(packageName);
if (pkg == null) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null) {
pkg = ps.pkg;
}
@@ -18509,7 +18504,7 @@
} else {
flags = 0;
}
- prepareAppDataContentsLIF(pkg, userId, flags);
+ prepareAppDataContentsLIF(pkg, ps, userId, flags);
return true;
}
@@ -19966,7 +19961,7 @@
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
|| newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
- if (!enableCompressedPackage(deletedPkg)) {
+ if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
return;
}
}
@@ -19983,7 +19978,7 @@
// We're dealing with a component level state change
// First, verify that this is a valid class name.
AndroidPackage pkg = pkgSetting.pkg;
- if (pkg == null || !pkg.hasComponentClassName(className)) {
+ if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
if (pkg != null &&
pkg.getTargetSdkVersion() >=
Build.VERSION_CODES.JELLY_BEAN) {
@@ -21216,24 +21211,27 @@
ipw.println();
ipw.println("Dexopt state:");
ipw.increaseIndent();
- Collection<AndroidPackage> packages;
+ Collection<PackageSetting> pkgSettings;
if (packageName != null) {
- AndroidPackage targetPackage = mPackages.get(packageName);
- if (targetPackage != null) {
- packages = Collections.singletonList(targetPackage);
+ PackageSetting targetPkgSetting = mSettings.mPackages.get(packageName);
+ if (targetPkgSetting != null) {
+ pkgSettings = Collections.singletonList(targetPkgSetting);
} else {
ipw.println("Unable to find package: " + packageName);
return;
}
} else {
- packages = mPackages.values();
+ pkgSettings = mSettings.mPackages.values();
}
- for (AndroidPackage pkg : packages) {
- ipw.println("[" + pkg.getPackageName() + "]");
+ for (PackageSetting pkgSetting : pkgSettings) {
+ if (pkgSetting.pkg == null) {
+ continue;
+ }
+ ipw.println("[" + pkgSetting.name + "]");
ipw.increaseIndent();
- mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
- mDexManager.getPackageUseInfoOrDefault(pkg.getPackageName()));
+ mPackageDexOptimizer.dumpDexoptState(ipw, pkgSetting.pkg, pkgSetting,
+ mDexManager.getPackageUseInfoOrDefault(pkgSetting.pkg.getPackageName()));
ipw.decreaseIndent();
}
}
@@ -21339,7 +21337,7 @@
final int[] packageUids = new int[size];
for (int i = 0; i < size; i++) {
final AndroidPackage pkg = packages.get(i);
- packageNames[i] = pkg.getAppInfoPackageName();
+ packageNames[i] = pkg.getPackageName();
packageUids[i] = pkg.getUid();
}
sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
@@ -21803,16 +21801,17 @@
final int appId = UserHandle.getAppId(pkg.getUid());
- Preconditions.checkNotNull(pkg.getSeInfo());
+ String pkgSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
- final String seInfo =
- pkg.getSeInfo() + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
+ Preconditions.checkNotNull(pkgSeInfo);
+
+ final String seInfo = pkgSeInfo + (pkg.getSeInfoUser() != null ? pkg.getSeInfoUser() : "");
long ceDataInode = -1;
try {
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId, flags,
appId, seInfo, pkg.getTargetSdkVersion());
} catch (InstallerException e) {
- if (pkg.isSystemApp()) {
+ if (pkg.isSystem()) {
logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+ ", but trying to recover: " + e);
destroyAppDataLeafLIF(pkg, userId, flags);
@@ -21857,18 +21856,20 @@
}
}
- prepareAppDataContentsLeafLIF(pkg, userId, flags);
+ prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
}
- private void prepareAppDataContentsLIF(AndroidPackage pkg, int userId, int flags) {
+ private void prepareAppDataContentsLIF(AndroidPackage pkg, @Nullable PackageSetting pkgSetting,
+ int userId, int flags) {
if (pkg == null) {
Slog.wtf(TAG, "Package was null!", new Throwable());
return;
}
- prepareAppDataContentsLeafLIF(pkg, userId, flags);
+ prepareAppDataContentsLeafLIF(pkg, pkgSetting, userId, flags);
}
- private void prepareAppDataContentsLeafLIF(AndroidPackage pkg, int userId, int flags) {
+ private void prepareAppDataContentsLeafLIF(AndroidPackage pkg,
+ @Nullable PackageSetting pkgSetting, int userId, int flags) {
final String volumeUuid = pkg.getVolumeUuid();
final String packageName = pkg.getPackageName();
@@ -21876,7 +21877,8 @@
// Create a native library symlink only if we have native libraries
// and if the native libraries are 32 bit libraries. We do not provide
// this symlink for 64 bit libraries.
- if (pkg.getPrimaryCpuAbi() != null && !VMRuntime.is64BitAbi(pkg.getPrimaryCpuAbi())) {
+ String primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
+ if (primaryCpuAbi != null && !VMRuntime.is64BitAbi(primaryCpuAbi)) {
final String nativeLibPath = pkg.getNativeLibraryDir();
try {
mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName,
@@ -22065,7 +22067,7 @@
|| shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) {
throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
- if (pkg.isSystemApp()) {
+ if (pkg.isSystem()) {
throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
"Cannot move system application");
}
@@ -22091,7 +22093,7 @@
throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
"Package already moved to " + volumeUuid);
}
- if (pkg.isInternal() && isPackageDeviceAdminOnAnyUser(packageName)) {
+ if (!pkg.isExternalStorage() && isPackageDeviceAdminOnAnyUser(packageName)) {
throw new PackageManagerException(MOVE_FAILED_DEVICE_ADMIN,
"Device admin cannot be moved");
}
@@ -22101,17 +22103,21 @@
"Failed to move already frozen package");
}
- isCurrentLocationExternal = isExternal(pkg);
+ isCurrentLocationExternal = pkg.isExternalStorage();
codeFile = new File(pkg.getCodePath());
installSource = ps.installSource;
packageAbiOverride = ps.cpuAbiOverrideString;
appId = UserHandle.getAppId(pkg.getUid());
- seinfo = pkg.getSeInfo();
+ seinfo = AndroidPackageUtils.getSeInfo(pkg, ps);
label = String.valueOf(pm.getApplicationLabel(pkg.toAppInfoWithoutState()));
targetSdkVersion = pkg.getTargetSdkVersion();
freezer = freezePackage(packageName, "movePackageInternal");
installedUserIds = ps.queryInstalledUsers(mUserManager.getUserIds(), true);
- fromCodePath = pkg.getCodePath();
+ if (codeFile.getParentFile().getName().startsWith(RANDOM_DIR_PREFIX)) {
+ fromCodePath = codeFile.getParentFile().getAbsolutePath();
+ } else {
+ fromCodePath = codeFile.getAbsolutePath();
+ }
}
final Bundle extras = new Bundle();
@@ -22238,9 +22244,8 @@
}
}).start();
- final String dataAppName = codeFile.getName();
move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName,
- dataAppName, appId, seinfo, targetSdkVersion, fromCodePath);
+ appId, seinfo, targetSdkVersion, fromCodePath);
} else {
move = null;
}
@@ -22281,15 +22286,15 @@
final StorageManager storage = mInjector.getStorageManager();;
VolumeInfo volume = storage.findVolumeByUuid(pkg.getStorageUuid().toString());
- int packageExternalStorageType = getPackageExternalStorageType(volume, isExternal(pkg));
+ int packageExternalStorageType = getPackageExternalStorageType(volume, pkg.isExternalStorage());
- if (!isPreviousLocationExternal && isExternal(pkg)) {
+ if (!isPreviousLocationExternal && pkg.isExternalStorage()) {
// Move from internal to external storage.
FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
packageExternalStorageType,
FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
packageName);
- } else if (isPreviousLocationExternal && !isExternal(pkg)) {
+ } else if (isPreviousLocationExternal && !pkg.isExternalStorage()) {
// Move from external to internal storage.
FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
packageExternalStorageType,
@@ -23013,7 +23018,7 @@
}
@Override
- public Object getDisabledSystemPackage(@NonNull String packageName) {
+ public PackageSetting getDisabledSystemPackage(@NonNull String packageName) {
synchronized (mLock) {
return mSettings.getDisabledSystemPkgLPr(packageName);
}
@@ -23363,7 +23368,7 @@
}
@Override
- public boolean isEnabledAndMatches(ParsedComponent component, int flags, int userId) {
+ public boolean isEnabledAndMatches(ParsedMainComponent component, int flags, int userId) {
synchronized (mLock) {
AndroidPackage pkg = getPackage(component.getPackageName());
return mSettings.isEnabledAndMatchLPr(pkg, component, flags, userId);
@@ -23455,11 +23460,7 @@
public boolean isPackagePersistent(String packageName) {
synchronized (mLock) {
AndroidPackage pkg = mPackages.get(packageName);
- return pkg != null
- ? ((pkg.getFlags() & (ApplicationInfo.FLAG_SYSTEM
- | ApplicationInfo.FLAG_PERSISTENT)) ==
- (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_PERSISTENT))
- : false;
+ return pkg != null && pkg.isSystem() && pkg.isPersistent();
}
}
@@ -23690,6 +23691,15 @@
}
@Override
+ public void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
+ synchronized (mLock) {
+ for (int index = 0; index < mSettings.mPackages.size(); index++) {
+ actionLocked.accept(mSettings.mPackages.valueAt(index));
+ }
+ }
+ }
+
+ @Override
public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
@UserIdInt int userId) {
PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
@@ -24185,15 +24195,18 @@
boolean canHaveOatDir(String packageName) {
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
- if (p == null) {
+ PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ if (p == null || pkgSetting == null) {
return false;
}
- return p.canHaveOatDir();
+ return AndroidPackageUtils.canHaveOatDir(p,
+ pkgSetting.getPkgState().isUpdatedSystemApp());
}
}
- private String getOatDir(AndroidPackage pkg) {
- if (!pkg.canHaveOatDir()) {
+ private String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) {
+ if (!AndroidPackageUtils.canHaveOatDir(pkg,
+ pkgSetting.getPkgState().isUpdatedSystemApp())) {
return null;
}
File codePath = new File(pkg.getCodePath());
@@ -24208,13 +24221,16 @@
final List<String> codePaths;
final String oatDir;
final AndroidPackage pkg;
+ final PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
+ pkgSetting = mSettings.getPackageLPr(packageName);
}
- instructionSets = getAppDexInstructionSets(pkg.getPrimaryCpuAbi(),
- pkg.getSecondaryCpuAbi());
- codePaths = pkg.getAllCodePaths();
- oatDir = getOatDir(pkg);
+ instructionSets = getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
+ codePaths = AndroidPackageUtils.getAllCodePaths(pkg);
+ oatDir = getOatDir(pkg, pkgSetting);
for (String codePath : codePaths) {
for (String isa : instructionSets) {
@@ -24241,8 +24257,8 @@
if (PackageManagerServiceUtils
.isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
downgradeTimeThresholdMillis, packageUseInfo,
- pkg.getLatestPackageUseTimeInMills(),
- pkg.getLatestForegroundPackageUseTimeInMills())) {
+ ps.getPkgState().getLatestPackageUseTimeInMills(),
+ ps.getPkgState().getLatestForegroundPackageUseTimeInMills())) {
unusedPackages.add(pkg.getPackageName());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a9035b2..91afd84 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -40,8 +40,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
@@ -66,6 +65,7 @@
import com.android.server.EventLogTags;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionsState;
import dalvik.system.VMRuntime;
@@ -94,6 +94,8 @@
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.zip.GZIPInputStream;
@@ -105,6 +107,9 @@
public class PackageManagerServiceUtils {
private final static long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;
+ public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG =
+ pkgSetting -> pkgSetting.pkg == null;
+
private static ArraySet<String> getPackageNamesForIntent(Intent intent, int userId) {
List<ResolveInfo> ris = null;
try {
@@ -124,40 +129,43 @@
// Sort a list of apps by their last usage, most recently used apps first. The order of
// packages without usage data is undefined (but they will be sorted after the packages
// that do have usage data).
- public static void sortPackagesByUsageDate(List<AndroidPackage> pkgs,
+ public static void sortPackagesByUsageDate(List<PackageSetting> pkgSettings,
PackageManagerService packageManagerService) {
if (!packageManagerService.isHistoricalPackageUsageAvailable()) {
return;
}
- Collections.sort(pkgs, (pkg1, pkg2) ->
- Long.compare(pkg2.getLatestForegroundPackageUseTimeInMills(),
- pkg1.getLatestForegroundPackageUseTimeInMills()));
+ Collections.sort(pkgSettings, (pkgSetting1, pkgSetting2) ->
+ Long.compare(
+ pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills(),
+ pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills())
+ );
}
// Apply the given {@code filter} to all packages in {@code packages}. If tested positive, the
// package will be removed from {@code packages} and added to {@code result} with its
// dependencies. If usage data is available, the positive packages will be sorted by usage
// data (with {@code sortTemp} as temporary storage).
- private static void applyPackageFilter(Predicate<AndroidPackage> filter,
- Collection<AndroidPackage> result,
- Collection<AndroidPackage> packages,
- @NonNull List<AndroidPackage> sortTemp,
+ private static void applyPackageFilter(
+ Predicate<PackageSetting> filter,
+ Collection<PackageSetting> result,
+ Collection<PackageSetting> packages,
+ @NonNull List<PackageSetting> sortTemp,
PackageManagerService packageManagerService) {
- for (AndroidPackage pkg : packages) {
- if (filter.test(pkg)) {
- sortTemp.add(pkg);
+ for (PackageSetting pkgSetting : packages) {
+ if (filter.test(pkgSetting)) {
+ sortTemp.add(pkgSetting);
}
}
sortPackagesByUsageDate(sortTemp, packageManagerService);
packages.removeAll(sortTemp);
- for (AndroidPackage pkg : sortTemp) {
- result.add(pkg);
+ for (PackageSetting pkgSetting : sortTemp) {
+ result.add(pkgSetting);
- Collection<AndroidPackage> deps =
- packageManagerService.findSharedNonSystemLibraries(pkg);
+ List<PackageSetting> deps =
+ packageManagerService.findSharedNonSystemLibraries(pkgSetting);
if (!deps.isEmpty()) {
deps.removeAll(result);
result.addAll(deps);
@@ -170,74 +178,79 @@
// Sort apps by importance for dexopt ordering. Important apps are given
// more priority in case the device runs out of space.
- public static List<AndroidPackage> getPackagesForDexopt(
- Collection<AndroidPackage> packages,
+ public static List<PackageSetting> getPackagesForDexopt(
+ Collection<PackageSetting> packages,
PackageManagerService packageManagerService) {
return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
}
- public static List<AndroidPackage> getPackagesForDexopt(
- Collection<AndroidPackage> packages,
+ public static List<PackageSetting> getPackagesForDexopt(
+ Collection<PackageSetting> pkgSettings,
PackageManagerService packageManagerService,
boolean debug) {
- ArrayList<AndroidPackage> remainingPkgs = new ArrayList<>(packages);
- LinkedList<AndroidPackage> result = new LinkedList<>();
- ArrayList<AndroidPackage> sortTemp = new ArrayList<>(remainingPkgs.size());
+ List<PackageSetting> result = new LinkedList<>();
+ ArrayList<PackageSetting> remainingPkgSettings = new ArrayList<>(pkgSettings);
+
+ // First, remove all settings without available packages
+ remainingPkgSettings.removeIf(REMOVE_IF_NULL_PKG);
+
+ ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
// Give priority to core apps.
- applyPackageFilter((pkg) -> pkg.isCoreApp(), result, remainingPkgs, sortTemp,
+ applyPackageFilter(pkgSetting -> pkgSetting.pkg.isCoreApp(), result, remainingPkgSettings, sortTemp,
packageManagerService);
// Give priority to system apps that listen for pre boot complete.
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- applyPackageFilter((pkg) -> pkgNames.contains(pkg.getPackageName()), result, remainingPkgs,
- sortTemp, packageManagerService);
+ applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.name), result,
+ remainingPkgSettings, sortTemp, packageManagerService);
// Give priority to apps used by other apps.
DexManager dexManager = packageManagerService.getDexManager();
- applyPackageFilter((pkg) ->
- dexManager.getPackageUseInfoOrDefault(pkg.getPackageName())
+ applyPackageFilter(pkgSetting ->
+ dexManager.getPackageUseInfoOrDefault(pkgSetting.name)
.isAnyCodePathUsedByOtherApps(),
- result, remainingPkgs, sortTemp, packageManagerService);
+ result, remainingPkgSettings, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps.
// TODO: add a property to control this?
- Predicate<AndroidPackage> remainingPredicate;
- if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
+ Predicate<PackageSetting> remainingPredicate;
+ if (!remainingPkgSettings.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
if (debug) {
Log.i(TAG, "Looking at historical package use");
}
// Get the package that was used last.
- AndroidPackage lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
- Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
- pkg2.getLatestForegroundPackageUseTimeInMills()));
+ PackageSetting lastUsed = Collections.max(remainingPkgSettings,
+ (pkgSetting1, pkgSetting2) -> Long.compare(
+ pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
+ pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
if (debug) {
- Log.i(TAG, "Taking package " + lastUsed.getPackageName()
+ Log.i(TAG, "Taking package " + lastUsed.name
+ " as reference in time use");
}
- long estimatedPreviousSystemUseTime =
- lastUsed.getLatestForegroundPackageUseTimeInMills();
+ long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
+ .getLatestForegroundPackageUseTimeInMills();
// Be defensive if for some reason package usage has bogus data.
if (estimatedPreviousSystemUseTime != 0) {
final long cutoffTime = estimatedPreviousSystemUseTime - SEVEN_DAYS_IN_MILLISECONDS;
- remainingPredicate =
- (pkg) -> pkg.getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
+ remainingPredicate = pkgSetting -> pkgSetting.getPkgState()
+ .getLatestForegroundPackageUseTimeInMills() >= cutoffTime;
} else {
// No meaningful historical info. Take all.
- remainingPredicate = (pkg) -> true;
+ remainingPredicate = pkgSetting -> true;
}
- sortPackagesByUsageDate(remainingPkgs, packageManagerService);
+ sortPackagesByUsageDate(remainingPkgSettings, packageManagerService);
} else {
// No historical info. Take all.
- remainingPredicate = (pkg) -> true;
+ remainingPredicate = pkgSetting -> true;
}
- applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
+ applyPackageFilter(remainingPredicate, result, remainingPkgSettings, sortTemp,
packageManagerService);
if (debug) {
Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
- Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
+ Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings));
}
return result;
@@ -290,13 +303,13 @@
}
}
- public static String packagesToString(Collection<AndroidPackage> c) {
+ public static String packagesToString(List<PackageSetting> pkgSettings) {
StringBuilder sb = new StringBuilder();
- for (AndroidPackage pkg : c) {
+ for (int index = 0; index < pkgSettings.size(); index++) {
if (sb.length() > 0) {
sb.append(", ");
}
- sb.append(pkg.getPackageName());
+ sb.append(pkgSettings.get(index).name);
}
return sb.toString();
}
@@ -543,23 +556,16 @@
*/
private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
PackageSetting disabledPkgSetting) {
- try {
- ApkParseUtils.collectCertificates(disabledPkgSetting.pkg, true /* skipVerify */);
- if (pkgSetting.signatures.mSigningDetails.checkCapability(
- disabledPkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
- || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
- pkgSetting.signatures.mSigningDetails,
- PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
- return true;
- } else {
- logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
- pkgSetting.name);
- return false;
- }
- } catch (PackageParserException e) {
- logCriticalInfo(Log.ERROR, "Failed to collect cert for " + pkgSetting.name + ": " +
- e.getMessage());
+ if (pkgSetting.signatures.mSigningDetails.checkCapability(
+ disabledPkgSetting.signatures.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
+ || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
+ pkgSetting.signatures.mSigningDetails,
+ PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
+ return true;
+ } else {
+ logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
+ pkgSetting.name);
return false;
}
}
@@ -642,8 +648,8 @@
}
}
// Check for shared user signatures
- if (pkgSetting.sharedUser != null
- && pkgSetting.sharedUser.signatures.mSigningDetails
+ if (pkgSetting.getSharedUser() != null
+ && pkgSetting.getSharedUser().signatures.mSigningDetails
!= PackageParser.SigningDetails.UNKNOWN) {
// Already existing package. Make sure signatures match. In case of signing certificate
@@ -653,24 +659,24 @@
// with being sharedUser with the existing signing cert.
boolean match =
parsedSignatures.checkCapability(
- pkgSetting.sharedUser.signatures.mSigningDetails,
+ pkgSetting.getSharedUser().signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
- || pkgSetting.sharedUser.signatures.mSigningDetails.checkCapability(
+ || pkgSetting.getSharedUser().signatures.mSigningDetails.checkCapability(
parsedSignatures,
PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
if (!match && compareCompat) {
match = matchSignaturesCompat(
- packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
+ packageName, pkgSetting.getSharedUser().signatures, parsedSignatures);
}
if (!match && compareRecover) {
match =
matchSignaturesRecover(packageName,
- pkgSetting.sharedUser.signatures.mSigningDetails,
+ pkgSetting.getSharedUser().signatures.mSigningDetails,
parsedSignatures,
PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID)
|| matchSignaturesRecover(packageName,
parsedSignatures,
- pkgSetting.sharedUser.signatures.mSigningDetails,
+ pkgSetting.getSharedUser().signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID);
compatMatch |= match;
}
@@ -678,7 +684,7 @@
throw new PackageManagerException(INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
+ " has no signatures that match those in shared user "
- + pkgSetting.sharedUser.name + "; ignoring!");
+ + pkgSetting.getSharedUser().name + "; ignoring!");
}
}
return compatMatch;
@@ -823,11 +829,11 @@
* Checks whenever downgrade of an app is permitted.
*
* @param installFlags flags of the current install.
- * @param applicationFlags flags of the currently installed version of the app.
+ * @param isAppDebuggable if the currently installed version of the app is debuggable.
* @return {@code true} if downgrade is permitted according to the {@code installFlags} and
* {@code applicationFlags}.
*/
- public static boolean isDowngradePermitted(int installFlags, int applicationFlags) {
+ public static boolean isDowngradePermitted(int installFlags, boolean isAppDebuggable) {
// If installed, the package will get access to data left on the device by its
// predecessor. As a security measure, this is permitted only if this is not a
// version downgrade or if the predecessor package is marked as debuggable and
@@ -849,9 +855,7 @@
if (!downgradeRequested) {
return false;
}
- final boolean isDebuggable =
- Build.IS_DEBUGGABLE || ((applicationFlags
- & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
+ final boolean isDebuggable = Build.IS_DEBUGGABLE || isAppDebuggable;
if (isDebuggable) {
return true;
}
@@ -915,8 +919,8 @@
*/
public static PermissionsState getPermissionsState(
PackageManagerInternal packageManagerInternal, AndroidPackage pkg) {
- final PackageSetting packageSetting =
- (PackageSetting) packageManagerInternal.getPackageSetting(pkg.getPackageName());
+ final PackageSetting packageSetting = packageManagerInternal.getPackageSetting(
+ pkg.getPackageName());
if (packageSetting == null) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index d83e6f4..2bd1a26 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -16,18 +16,19 @@
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ParsedPackage;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.pkg.PackageStateUnserialized;
import java.io.File;
import java.util.ArrayList;
@@ -39,8 +40,7 @@
/**
* Settings data for a particular package we know about.
*/
-public final class PackageSetting extends PackageSettingBase implements
- ParsedPackage.PackageSettingCallback {
+public class PackageSetting extends PackageSettingBase {
int appId;
public AndroidPackage pkg;
@@ -65,6 +65,9 @@
@Nullable
Map<String, ArraySet<String>> mimeGroups;
+ @NonNull
+ private PackageStateUnserialized pkgState = new PackageStateUnserialized();
+
PackageSetting(String name, String realName, File codePath, File resourcePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString,
@@ -223,10 +226,6 @@
return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
- public boolean isUpdatedSystem() {
- return (pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
- }
-
@Override
public boolean isSharedUser() {
return sharedUser != null;
@@ -324,9 +323,8 @@
updateMimeGroups(mimeGroupNames);
}
- // TODO(b/135203078): Move to constructor
- @Override
- public void setAndroidPackage(AndroidPackage pkg) {
- this.pkg = pkg;
+ @NonNull
+ public PackageStateUnserialized getPkgState() {
+ return pkgState;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 7d95b19..18bc879 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -78,12 +78,12 @@
/**
* The primary CPU abi for this package.
*/
- String primaryCpuAbiString;
+ public String primaryCpuAbiString;
/**
* The secondary CPU abi for this package.
*/
- String secondaryCpuAbiString;
+ public String secondaryCpuAbiString;
/**
* The install time CPU override, if any. This value is written at install time
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
index ce2c9e7..ef37a20 100644
--- a/services/core/java/com/android/server/pm/PackageUsage.java
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -20,11 +20,12 @@
import static android.os.Process.SYSTEM_UID;
import android.content.pm.PackageManager;
-import android.content.pm.parsing.AndroidPackage;
import android.os.FileUtils;
import android.util.AtomicFile;
import android.util.Log;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
import libcore.io.IoUtils;
import java.io.BufferedInputStream;
@@ -36,7 +37,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Map;
-class PackageUsage extends AbstractStatsBase<Map<String, AndroidPackage>> {
+class PackageUsage extends AbstractStatsBase<Map<String, PackageSetting>> {
private static final String USAGE_FILE_MAGIC = "PACKAGE_USAGE__VERSION_";
private static final String USAGE_FILE_MAGIC_VERSION_1 = USAGE_FILE_MAGIC + "1";
@@ -52,7 +53,7 @@
}
@Override
- protected void writeInternal(Map<String, AndroidPackage> packages) {
+ protected void writeInternal(Map<String, PackageSetting> pkgSettings) {
AtomicFile file = getFile();
FileOutputStream f = null;
try {
@@ -66,13 +67,14 @@
sb.append('\n');
out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
- for (AndroidPackage pkg : packages.values()) {
- if (pkg.getLatestPackageUseTimeInMills() == 0L) {
+ for (PackageSetting pkgSetting : pkgSettings.values()) {
+ if (pkgSetting.getPkgState().getLatestPackageUseTimeInMills() == 0L) {
continue;
}
sb.setLength(0);
- sb.append(pkg.getPackageName());
- for (long usageTimeInMillis : pkg.getLastPackageUsageTimeInMills()) {
+ sb.append(pkgSetting.name);
+ for (long usageTimeInMillis : pkgSetting.getPkgState()
+ .getLastPackageUsageTimeInMills()) {
sb.append(' ');
sb.append(usageTimeInMillis);
}
@@ -90,7 +92,7 @@
}
@Override
- protected void readInternal(Map<String, AndroidPackage> packages) {
+ protected void readInternal(Map<String, PackageSetting> pkgSettings) {
AtomicFile file = getFile();
BufferedInputStream in = null;
try {
@@ -101,9 +103,9 @@
if (firstLine == null) {
// Empty file. Do nothing.
} else if (USAGE_FILE_MAGIC_VERSION_1.equals(firstLine)) {
- readVersion1LP(packages, in, sb);
+ readVersion1LP(pkgSettings, in, sb);
} else {
- readVersion0LP(packages, in, sb, firstLine);
+ readVersion0LP(pkgSettings, in, sb, firstLine);
}
} catch (FileNotFoundException expected) {
mIsHistoricalPackageUsageAvailable = false;
@@ -114,7 +116,7 @@
}
}
- private void readVersion0LP(Map<String, AndroidPackage> packages, InputStream in,
+ private void readVersion0LP(Map<String, PackageSetting> pkgSettings, InputStream in,
StringBuffer sb, String firstLine)
throws IOException {
// Initial version of the file had no version number and stored one
@@ -128,8 +130,8 @@
}
String packageName = tokens[0];
- AndroidPackage pkg = packages.get(packageName);
- if (pkg == null) {
+ PackageSetting pkgSetting = pkgSettings.get(packageName);
+ if (pkgSetting == null) {
continue;
}
@@ -137,12 +139,12 @@
for (int reason = 0;
reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
reason++) {
- pkg.mutate().setLastPackageUsageTimeInMills(reason, timestamp);
+ pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, timestamp);
}
}
}
- private void readVersion1LP(Map<String, AndroidPackage> packages, InputStream in,
+ private void readVersion1LP(Map<String, PackageSetting> pkgSettings, InputStream in,
StringBuffer sb) throws IOException {
// Version 1 of the file started with the corresponding version
// number and then stored a package name and eight timestamps per line.
@@ -154,15 +156,15 @@
}
String packageName = tokens[0];
- AndroidPackage pkg = packages.get(packageName);
- if (pkg == null) {
+ PackageSetting pkgSetting = pkgSettings.get(packageName);
+ if (pkgSetting == null) {
continue;
}
for (int reason = 0;
reason < PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT;
reason++) {
- pkg.mutate().setLastPackageUsageTimeInMills(reason,
+ pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason,
parseAsLong(tokens[reason + 1]));
}
}
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 448dad0..21334c0 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -19,12 +19,13 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Process;
import android.os.Trace;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ConcurrentUtils;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
@@ -50,11 +51,11 @@
Process.THREAD_PRIORITY_FOREGROUND);
}
- private final PackageParser mPackageParser;
+ private final PackageParser2 mPackageParser;
private final ExecutorService mExecutorService;
- ParallelPackageParser(PackageParser packageParser, ExecutorService executorService) {
+ ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
mPackageParser = packageParser;
mExecutorService = executorService;
}
@@ -125,6 +126,6 @@
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageParser.PackageParserException {
- return mPackageParser.parseParsedPackage(scanFile, parseFlags, true);
+ return mPackageParser.parsePackage(scanFile, parseFlags, true);
}
}
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
index ef29cb3..08a87d8 100644
--- a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
@@ -16,6 +16,9 @@
package com.android.server.pm;
+import android.annotation.NonNull;
+import android.content.IntentFilter;
+
import com.android.server.IntentResolver;
public class PersistentPreferredIntentResolver
@@ -26,6 +29,11 @@
}
@Override
+ protected IntentFilter getIntentFilter(@NonNull PersistentPreferredActivity input) {
+ return input;
+ }
+
+ @Override
protected boolean isPackageForFilter(String packageName, PersistentPreferredActivity filter) {
return packageName.equals(filter.mComponent.getPackageName());
}
diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
index bce24d7..a261e29 100644
--- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
@@ -16,6 +16,9 @@
package com.android.server.pm;
+import android.annotation.NonNull;
+import android.content.IntentFilter;
+
import java.io.PrintWriter;
import com.android.server.IntentResolver;
@@ -37,4 +40,9 @@
PreferredActivity filter) {
filter.mPref.dump(out, prefix, filter);
}
+
+ @Override
+ protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) {
+ return input;
+ }
}
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 466f19c..67e1994 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -18,15 +18,14 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;
import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 60c8b94..42b2eeb 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -49,10 +49,12 @@
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils;
-import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedProcess;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -76,6 +78,7 @@
import android.util.AtomicFile;
import android.util.Log;
import android.util.LogPrinter;
+import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -96,6 +99,9 @@
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionSettings;
import com.android.server.pm.permission.PermissionsState;
@@ -479,10 +485,9 @@
}
final PackageSetting dp = mDisabledSysPackages.get(name);
// always make sure the system package code and resource paths dont change
- if (dp == null && p.pkg != null && p.pkg.isSystem() && !p.pkg.isUpdatedSystemApp()) {
- if(p.pkg != null) {
- p.pkg.mutate().setUpdatedSystemApp(true);
- }
+ if (dp == null && p.pkg != null && p.pkg.isSystem()
+ && !p.getPkgState().isUpdatedSystemApp()) {
+ p.getPkgState().setUpdatedSystemApp(true);
final PackageSetting disabled;
if (replaced) {
// a little trick... when we install the new package, we don't
@@ -506,10 +511,7 @@
Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled");
return null;
}
- // Reset flag in ApplicationInfo object
- if(p.pkg != null) {
- p.pkg.mutate().setUpdatedSystemApp(false);
- }
+ p.getPkgState().setUpdatedSystemApp(false);
PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
p.secondaryCpuAbiString, p.cpuAbiOverrideString,
@@ -2673,15 +2675,19 @@
StringBuilder sb = new StringBuilder();
for (final PackageSetting pkg : mPackages.values()) {
- if (pkg.pkg == null || pkg.pkg.getDataDir() == null) {
+ // TODO(b/135203078): This doesn't handle multiple users
+ final String dataPath = pkg.pkg == null ? null :
+ PackageInfoWithoutStateUtils.getDataDir(pkg.pkg,
+ UserHandle.USER_SYSTEM).getAbsolutePath();
+
+ if (pkg.pkg == null || dataPath == null) {
if (!"android".equals(pkg.name)) {
Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
}
continue;
}
- final String dataPath = pkg.pkg.getDataDir();
- final boolean isDebug = (pkg.pkg.getFlags() & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ final boolean isDebug = pkg.pkg.isDebuggable();
final int[] gids = pkg.getPermissionsState().computeGids(userIds);
// Avoid any application that has a space in its path.
@@ -2712,7 +2718,7 @@
sb.append(isDebug ? " 1 " : " 0 ");
sb.append(dataPath);
sb.append(" ");
- sb.append(pkg.pkg.getSeInfo());
+ sb.append(AndroidPackageUtils.getSeInfo(pkg.pkg, pkg));
sb.append(" ");
if (gids != null && gids.length > 0) {
sb.append(gids[0]);
@@ -3154,14 +3160,14 @@
final PackageManagerInternal pmInternal =
LocalServices.getService(PackageManagerInternal.class);
for (PackageSetting ps : mPackages.values()) {
- if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0 && ps.pkg != null
- && ps.pkg.getPreferredActivityFilters() != null) {
- List<ComponentParseUtils.ParsedActivityIntentInfo> intents
+ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.pkg != null
+ && !ps.pkg.getPreferredActivityFilters().isEmpty()) {
+ List<Pair<String, ParsedIntentInfo>> intents
= ps.pkg.getPreferredActivityFilters();
for (int i=0; i<intents.size(); i++) {
- ComponentParseUtils.ParsedActivityIntentInfo aii = intents.get(i);
- applyDefaultPreferredActivityLPw(pmInternal, aii, new ComponentName(
- ps.name, aii.getClassName()), userId);
+ Pair<String, ParsedIntentInfo> pair = intents.get(i);
+ applyDefaultPreferredActivityLPw(pmInternal, pair.second, new ComponentName(
+ ps.name, pair.first), userId);
}
}
}
@@ -4129,7 +4135,7 @@
final boolean shouldInstall = ps.isSystem() &&
(skipPackageWhitelist || installablePackages.contains(ps.name)) &&
!ArrayUtils.contains(disallowedPackages, ps.name) &&
- !ps.pkg.isHiddenUntilInstalled();
+ !ps.getPkgState().isHiddenUntilInstalled();
// Only system apps are initially installed.
ps.setInstalled(shouldInstall, userHandle);
if (!shouldInstall) {
@@ -4140,7 +4146,7 @@
volumeUuids[i] = ps.volumeUuid;
names[i] = ps.name;
appIds[i] = ps.appId;
- seinfos[i] = ps.pkg.getSeInfo();
+ seinfos[i] = AndroidPackageUtils.getSeInfo(ps.pkg, ps);
targetSdkVersions[i] = ps.pkg.getTargetSdkVersion();
}
}
@@ -4281,7 +4287,7 @@
return userState.isMatch(componentInfo, flags);
}
- boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedComponent component, int flags,
+ boolean isEnabledAndMatchLPr(AndroidPackage pkg, ParsedMainComponent component, int flags,
int userId) {
final PackageSetting ps = mPackages.get(component.getPackageName());
if (ps == null) return false;
@@ -4596,58 +4602,60 @@
pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion);
// TODO(b/135203078): Is there anything to print here with AppInfo removed?
pw.print(prefix); pw.print(" applicationInfo=");
- pw.println(pkg.toAppInfoWithoutState().toString());
- pw.print(prefix); pw.print(" flags="); printFlags(pw, pkg.getFlags(),
- FLAG_DUMP_SPEC); pw.println();
- if (pkg.getPrivateFlags() != 0) {
+ pw.println(pkg.toAppInfoToString());
+ pw.print(prefix); pw.print(" flags=");
+ printFlags(pw, PackageInfoUtils.appInfoFlags(pkg, ps), FLAG_DUMP_SPEC); pw.println();
+ int privateFlags = PackageInfoUtils.appInfoPrivateFlags(pkg, ps);
+ if (privateFlags != 0) {
pw.print(prefix); pw.print(" privateFlags="); printFlags(pw,
- pkg.getPrivateFlags(), PRIVATE_FLAG_DUMP_SPEC); pw.println();
+ privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
}
pw.print(prefix); pw.print(" forceQueryable="); pw.print(ps.pkg.isForceQueryable());
if (ps.forceQueryableOverride) {
pw.print(" (override=true)");
}
pw.println();
- if (ps.pkg.getQueriesPackages() != null) {
+ if (ps.pkg.getQueriesPackages().isEmpty()) {
pw.append(prefix).append(" queriesPackages=").println(ps.pkg.getQueriesPackages());
}
- if (ps.pkg.getQueriesIntents() != null) {
+ if (!ps.pkg.getQueriesIntents().isEmpty()) {
pw.append(prefix).append(" queriesIntents=").println(ps.pkg.getQueriesIntents());
}
- pw.print(prefix); pw.print(" dataDir="); pw.println(ps.pkg.getDataDir());
+ File dataDir = PackageInfoWithoutStateUtils.getDataDir(pkg, UserHandle.myUserId());
+ pw.print(prefix); pw.print(" dataDir="); pw.println(dataDir.getAbsolutePath());
pw.print(prefix); pw.print(" supportsScreens=[");
boolean first = true;
- if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) != 0) {
+ if (pkg.isSupportsSmallScreens()) {
if (!first)
pw.print(", ");
first = false;
pw.print("small");
}
- if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) != 0) {
+ if (pkg.isSupportsNormalScreens()) {
if (!first)
pw.print(", ");
first = false;
pw.print("medium");
}
- if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+ if (pkg.isSupportsLargeScreens()) {
if (!first)
pw.print(", ");
first = false;
pw.print("large");
}
- if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
+ if (pkg.isSupportsExtraLargeScreens()) {
if (!first)
pw.print(", ");
first = false;
pw.print("xlarge");
}
- if ((pkg.getFlags() & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+ if (pkg.isResizeable()) {
if (!first)
pw.print(", ");
first = false;
pw.print("resizeable");
}
- if ((pkg.getFlags() & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
+ if (pkg.isAnyDensity()) {
if (!first)
pw.print(", ");
first = false;
@@ -4669,18 +4677,17 @@
pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
}
- final List<String> usesLibraries = pkg.getUsesLibraries();
- if (usesLibraries != null && usesLibraries.size() > 0) {
+ List<String> usesLibraries = pkg.getUsesLibraries();
+ if (usesLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesLibraries:");
for (int i=0; i< usesLibraries.size(); i++) {
pw.print(prefix); pw.print(" "); pw.println(usesLibraries.get(i));
}
}
- final List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
- final long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
- if (usesStaticLibraries != null
- && usesStaticLibraries.size() > 0) {
+ List<String> usesStaticLibraries = pkg.getUsesStaticLibraries();
+ long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions();
+ if (usesStaticLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesStaticLibraries:");
for (int i=0; i< usesStaticLibraries.size(); i++) {
pw.print(prefix); pw.print(" ");
@@ -4689,9 +4696,8 @@
}
}
- final List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
- if (usesOptionalLibraries != null
- && usesOptionalLibraries.size() > 0) {
+ List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
+ if (usesOptionalLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesOptionalLibraries:");
for (int i=0; i< usesOptionalLibraries.size(); i++) {
pw.print(prefix); pw.print(" ");
@@ -4699,24 +4705,22 @@
}
}
- final String[] usesLibraryFiles = pkg.getUsesLibraryFiles();
- if (usesLibraryFiles != null
- && usesLibraryFiles.length > 0) {
+ List<String> usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles();
+ if (usesLibraryFiles.size() > 0) {
pw.print(prefix); pw.println(" usesLibraryFiles:");
- for (int i=0; i< usesLibraryFiles.length; i++) {
- pw.print(prefix); pw.print(" "); pw.println(usesLibraryFiles[i]);
+ for (int i=0; i< usesLibraryFiles.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(usesLibraryFiles.get(i));
}
}
- final ArrayMap<String, ComponentParseUtils.ParsedProcess> procs = pkg.getProcesses();
- if (procs != null) {
+ final Map<String, ParsedProcess> procs = pkg.getProcesses();
+ if (!procs.isEmpty()) {
pw.print(prefix); pw.println(" processes:");
- for (int i = 0; i < procs.size(); i++) {
- final ComponentParseUtils.ParsedProcess proc = procs.valueAt(i);
- pw.print(prefix); pw.print(" "); pw.println(proc.name);
- if (proc.deniedPermissions != null) {
- for (int j = 0; j < proc.deniedPermissions.size(); j++) {
+ for (ParsedProcess proc : procs.values()) {
+ pw.print(prefix); pw.print(" "); pw.println(proc.getName());
+ if (proc.getDeniedPermissions() != null) {
+ for (String deniedPermission : proc.getDeniedPermissions()) {
pw.print(prefix); pw.print(" deny: ");
- pw.println(proc.deniedPermissions.valueAt(j));
+ pw.println(deniedPermission);
}
}
}
@@ -4751,7 +4755,7 @@
pw.print(prefix); pw.print(" overlayCategory="); pw.println(pkg.getOverlayCategory());
}
- if (pkg != null && pkg.getPermissions() != null && pkg.getPermissions().size() > 0) {
+ if (pkg != null && !pkg.getPermissions().isEmpty()) {
final List<ParsedPermission> perms = pkg.getPermissions();
pw.print(prefix); pw.println(" declared permissions:");
for (int i=0; i<perms.size(); i++) {
@@ -4762,14 +4766,14 @@
}
pw.print(prefix); pw.print(" "); pw.print(perm.getName());
pw.print(": prot=");
- pw.print(PermissionInfo.protectionToString(perm.protectionLevel));
- if ((perm.flags&PermissionInfo.FLAG_COSTS_MONEY) != 0) {
+ pw.print(PermissionInfo.protectionToString(perm.getProtectionLevel()));
+ if ((perm.getFlags() &PermissionInfo.FLAG_COSTS_MONEY) != 0) {
pw.print(", COSTS_MONEY");
}
- if ((perm.flags&PermissionInfo.FLAG_REMOVED) != 0) {
+ if ((perm.getFlags() &PermissionInfo.FLAG_REMOVED) != 0) {
pw.print(", HIDDEN");
}
- if ((perm.flags&PermissionInfo.FLAG_INSTALLED) != 0) {
+ if ((perm.getFlags() &PermissionInfo.FLAG_INSTALLED) != 0) {
pw.print(", INSTALLED");
}
pw.println();
@@ -5403,7 +5407,7 @@
packagePermissions, sharedUserPermissions);
}
- mPersistence.write(runtimePermissions, UserHandle.of(userId));
+ mPersistence.writeAsUser(runtimePermissions, UserHandle.of(userId));
}
@NonNull
@@ -5457,12 +5461,13 @@
}
public void deleteUserRuntimePermissionsFile(int userId) {
- mPersistence.delete(UserHandle.of(userId));
+ mPersistence.deleteAsUser(UserHandle.of(userId));
}
@GuardedBy("Settings.this.mLock")
public void readStateForUserSyncLPr(int userId) {
- RuntimePermissionsState runtimePermissions = mPersistence.read(UserHandle.of(userId));
+ RuntimePermissionsState runtimePermissions = mPersistence.readAsUser(UserHandle.of(
+ userId));
if (runtimePermissions == null) {
readLegacyStateForUserSyncLPr(userId);
writePermissionsForUserAsyncLPr(userId);
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index bbd319c..6103f558 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -18,19 +18,20 @@
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedProcess;
import android.service.pm.PackageServiceDumpProto;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import libcore.util.EmptyArray;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* Settings data for a particular shared user ID we know about.
@@ -53,7 +54,7 @@
final PackageSignatures signatures = new PackageSignatures();
Boolean signaturesChanged;
- ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;
+ ArrayMap<String, ParsedProcess> processes;
SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
super(_pkgFlags, _pkgPrivateFlags);
@@ -76,18 +77,18 @@
proto.end(token);
}
- void addProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> newProcs) {
+ void addProcesses(Map<String, ParsedProcess> newProcs) {
if (newProcs != null) {
final int numProcs = newProcs.size();
if (processes == null) {
processes = new ArrayMap<>(numProcs);
}
- for (int i = 0; i < numProcs; i++) {
- ComponentParseUtils.ParsedProcess newProc = newProcs.valueAt(i);
- ComponentParseUtils.ParsedProcess proc = processes.get(newProc.name);
+ for (String key : newProcs.keySet()) {
+ ParsedProcess newProc = newProcs.get(key);
+ ParsedProcess proc = processes.get(newProc.getName());
if (proc == null) {
- proc = new ComponentParseUtils.ParsedProcess(newProc);
- processes.put(newProc.name, proc);
+ proc = new ParsedProcess(newProc);
+ processes.put(newProc.getName(), proc);
} else {
proc.addStateFrom(newProc);
}
@@ -159,19 +160,24 @@
* restrictive selinux domain.
*/
public void fixSeInfoLocked() {
- final List<AndroidPackage> pkgList = getPackages();
- if (pkgList == null || pkgList.size() == 0) {
+ if (packages == null || packages.size() == 0) {
return;
}
-
- for (AndroidPackage pkg : pkgList) {
- if (pkg.getTargetSdkVersion() < seInfoTargetSdkVersion) {
- seInfoTargetSdkVersion = pkg.getTargetSdkVersion();
+ for (PackageSetting ps : packages) {
+ if ((ps == null) || (ps.pkg == null)) {
+ continue;
+ }
+ if (ps.pkg.getTargetSdkVersion() < seInfoTargetSdkVersion) {
+ seInfoTargetSdkVersion = ps.pkg.getTargetSdkVersion();
}
}
- for (AndroidPackage pkg : pkgList) {
- final boolean isPrivileged = isPrivileged() | pkg.isPrivileged();
- pkg.mutate().setSeInfo(SELinuxMMAC.getSeInfo(pkg, isPrivileged,
+
+ for (PackageSetting ps : packages) {
+ if ((ps == null) || (ps.pkg == null)) {
+ continue;
+ }
+ final boolean isPrivileged = isPrivileged() | ps.pkg.isPrivileged();
+ ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.pkg, isPrivileged,
seInfoTargetSdkVersion));
}
}
@@ -221,9 +227,9 @@
final int numProcs = sharedUser.processes.size();
this.processes = new ArrayMap<>(numProcs);
for (int i = 0; i < numProcs; i++) {
- ComponentParseUtils.ParsedProcess proc =
- new ComponentParseUtils.ParsedProcess(sharedUser.processes.valueAt(i));
- this.processes.put(proc.name, proc);
+ ParsedProcess proc =
+ new ParsedProcess(sharedUser.processes.valueAt(i));
+ this.processes.put(proc.getName(), proc);
}
} else {
this.processes = null;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c37ceb3..c8df5c7 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.ShortcutService.DumpFilter;
@@ -310,10 +311,67 @@
}
/**
- * Remove all shortcuts that aren't pinned, cached nor dynamic.
+ * Push a shortcut. If the max number of dynamic shortcuts is already reached, remove the
+ * shortcut with the lowest rank before adding the new shortcut.
*/
- private void removeOrphans() {
+ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
+ Preconditions.checkArgument(newShortcut.isEnabled(),
+ "pushDynamicShortcuts() cannot publish disabled shortcuts");
+
+ newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+
+ final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
+ boolean wasPinned = false;
+
+ if (oldShortcut == null) {
+ final ShortcutService service = mShortcutUser.mService;
+ final int maxShortcuts = service.getMaxActivityShortcuts();
+
+ final ArrayMap<ComponentName, ArrayList<ShortcutInfo>> all =
+ sortShortcutsToActivities();
+ final ArrayList<ShortcutInfo> activityShortcuts = all.get(newShortcut.getActivity());
+
+ if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) {
+ // Max has reached. Delete the shortcut with lowest rank.
+
+ // Sort by isManifestShortcut() and getRank().
+ Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator);
+
+ final ShortcutInfo shortcut = activityShortcuts.get(maxShortcuts - 1);
+ if (shortcut.isManifestShortcut()) {
+ // All shortcuts are manifest shortcuts and cannot be removed.
+ Slog.e(TAG, "Failed to remove manifest shortcut while pushing dynamic shortcut "
+ + newShortcut.getId());
+ return false;
+ }
+
+ deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
+ }
+ } else {
+ // It's an update case.
+ // Make sure the target is updatable. (i.e. should be mutable.)
+ oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
+
+ wasPinned = oldShortcut.isPinned();
+ }
+
+ // If it was originally pinned, the new one should be pinned too.
+ if (wasPinned) {
+ newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
+
+ forceReplaceShortcutInner(newShortcut);
+ return true;
+ }
+
+ /**
+ * Remove all shortcuts that aren't pinned, cached nor dynamic.
+ *
+ * @return List of removed shortcuts.
+ */
+ private List<ShortcutInfo> removeOrphans() {
ArrayList<String> removeList = null; // Lazily initialize.
+ List<ShortcutInfo> removedShortcuts = null;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -322,20 +380,26 @@
if (removeList == null) {
removeList = new ArrayList<>();
+ removedShortcuts = new ArrayList<>();
}
removeList.add(si.getId());
+ removedShortcuts.add(si);
}
if (removeList != null) {
for (int i = removeList.size() - 1; i >= 0; i--) {
forceDeleteShortcutInner(removeList.get(i));
}
}
+
+ return removedShortcuts;
}
/**
* Remove all dynamic shortcuts.
+ *
+ * @return List of shortcuts that actually got removed.
*/
- public void deleteAllDynamicShortcuts(boolean ignoreInvisible) {
+ public List<ShortcutInfo> deleteAllDynamicShortcuts(boolean ignoreInvisible) {
final long now = mShortcutUser.mService.injectCurrentTimeMillis();
boolean changed = false;
@@ -350,8 +414,9 @@
}
}
if (changed) {
- removeOrphans();
+ return removeOrphans();
}
+ return null;
}
/**
@@ -974,7 +1039,8 @@
s.verifyStates();
// This will send a notification to the launcher, and also save .
- s.packageShortcutsChanged(getPackageName(), getPackageUserId());
+ // TODO: List changed and removed manifest shortcuts and pass to packageShortcutsChanged()
+ s.packageShortcutsChanged(getPackageName(), getPackageUserId(), null, null);
return true; // true means changed.
}
@@ -1245,15 +1311,14 @@
*/
public void resolveResourceStrings() {
final ShortcutService s = mShortcutUser.mService;
- boolean changed = false;
+
+ List<ShortcutInfo> changedShortcuts = null;
Resources publisherRes = null;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
if (si.hasStringResources()) {
- changed = true;
-
if (publisherRes == null) {
publisherRes = getPackageResources();
if (publisherRes == null) {
@@ -1263,10 +1328,15 @@
si.resolveResourceStrings(publisherRes);
si.setTimestamp(s.injectCurrentTimeMillis());
+
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
}
}
- if (changed) {
- s.packageShortcutsChanged(getPackageName(), getPackageUserId());
+ if (!CollectionUtils.isEmpty(changedShortcuts)) {
+ s.packageShortcutsChanged(getPackageName(), getPackageUserId(), changedShortcuts, null);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 3e44de9..6fd997d 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -26,7 +26,6 @@
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -36,6 +35,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Handles {@link android.content.pm.ShortcutManager#requestPinShortcut} related tasks.
*/
@@ -452,6 +454,8 @@
final String launcherPackage = request.launcherPackage;
final String shortcutId = original.getId();
+ List<ShortcutInfo> changedShortcuts = null;
+
synchronized (mLock) {
if (!(mService.isUserUnlockedL(appUserId)
&& mService.isUserUnlockedL(request.launcherUserId))) {
@@ -506,8 +510,13 @@
Slog.d(TAG, "Pinning " + shortcutId);
}
+
launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
/*forPinRequest=*/ true);
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(original);
if (current == null) {
if (DEBUG) {
@@ -520,7 +529,7 @@
}
mService.verifyStates();
- mService.packageShortcutsChanged(appPackageName, appUserId);
+ mService.packageShortcutsChanged(appPackageName, appUserId, changedShortcuts, null);
return true;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 12f7d5c..66f3574 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -98,6 +98,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -133,6 +134,7 @@
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -276,6 +278,10 @@
private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
@GuardedBy("mLock")
+ private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
+ new ArrayList<>(1);
+
+ @GuardedBy("mLock")
private long mRawLastResetTime;
/**
@@ -1638,8 +1644,12 @@
* - Sends a notification to LauncherApps
* - Write to file
*/
- void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId) {
+ void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId,
+ @Nullable List<ShortcutInfo> addedOrUpdatedShortcuts,
+ @Nullable List<ShortcutInfo> removedShortcuts) {
notifyListeners(packageName, userId);
+ notifyShortcutChangeCallbacks(packageName, userId, addedOrUpdatedShortcuts,
+ removedShortcuts);
scheduleSaveUser(userId);
}
@@ -1667,6 +1677,34 @@
});
}
+ private void notifyShortcutChangeCallbacks(@NonNull String packageName, @UserIdInt int userId,
+ @Nullable List<ShortcutInfo> addedOrUpdatedShortcuts,
+ @Nullable List<ShortcutInfo> removedShortcuts) {
+ final UserHandle user = UserHandle.of(userId);
+ injectPostToHandler(() -> {
+ try {
+ final ArrayList<LauncherApps.ShortcutChangeCallback> copy;
+ synchronized (mLock) {
+ if (!isUserUnlockedL(userId)) {
+ return;
+ }
+
+ copy = new ArrayList<>(mShortcutChangeCallbacks);
+ }
+ for (int i = copy.size() - 1; i >= 0; i--) {
+ if (!CollectionUtils.isEmpty(addedOrUpdatedShortcuts)) {
+ copy.get(i).onShortcutsAddedOrUpdated(packageName, addedOrUpdatedShortcuts,
+ user);
+ }
+ if (!CollectionUtils.isEmpty(removedShortcuts)) {
+ copy.get(i).onShortcutsRemoved(packageName, removedShortcuts, user);
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ });
+ }
+
/**
* Clean up / validate an incoming shortcut.
* - Make sure all mandatory fields are set.
@@ -1761,6 +1799,8 @@
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
injectBinderCallingPid(), injectBinderCallingUid());
+ List<ShortcutInfo> removedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -1786,7 +1826,7 @@
}
// First, remove all un-pinned; dynamic shortcuts
- ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
@@ -1797,7 +1837,7 @@
// Lastly, adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(packageName, userId, newShortcuts, removedShortcuts);
verifyStates();
@@ -1816,6 +1856,8 @@
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
injectBinderCallingPid(), injectBinderCallingUid());
+ List<ShortcutInfo> changedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -1878,12 +1920,17 @@
if (replacingIcon || source.hasStringResources()) {
fixUpShortcutResourceNamesAndValues(target);
}
+
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(target);
}
// Lastly, adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(packageName, userId, changedShortcuts, null);
verifyStates();
@@ -1902,6 +1949,8 @@
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
injectBinderCallingPid(), injectBinderCallingUid());
+ List<ShortcutInfo> changedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -1933,12 +1982,17 @@
// Add it.
ps.addOrReplaceDynamicShortcut(newShortcut);
+
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(newShortcut);
}
// Lastly, adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(packageName, userId, changedShortcuts, null);
verifyStates();
@@ -1946,6 +2000,50 @@
}
@Override
+ public void pushDynamicShortcut(String packageName, ShortcutInfo shortcut,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ verifyShortcutInfoPackage(packageName, shortcut);
+
+ final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+ injectBinderCallingPid(), injectBinderCallingUid());
+
+ synchronized (mLock) {
+ throwIfUserLockedL(userId);
+
+ final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+
+ ps.ensureNotImmutable(shortcut.getId(), /*ignoreInvisible=*/ true);
+ fillInDefaultActivity(Arrays.asList(shortcut));
+
+ if (!shortcut.hasRank()) {
+ shortcut.setRank(0);
+ }
+ // Initialize the implicit ranks for ShortcutPackage.adjustRanks().
+ ps.clearAllImplicitRanks();
+ shortcut.setImplicitRank(0);
+
+ // Validate the shortcut.
+ fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false);
+
+ // When ranks are changing, we need to insert between ranks, so set the
+ // "rank changed" flag.
+ shortcut.setRankChanged();
+
+ // Push it.
+ if (!ps.pushDynamicShortcut(shortcut)) {
+ return;
+ }
+
+ // Lastly, adjust the ranks.
+ ps.adjustRanks();
+ }
+ packageShortcutsChanged(packageName, userId, Collections.singletonList(shortcut), null);
+
+ verifyStates();
+ }
+
+ @Override
public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
IntentSender resultIntent, int userId) {
Objects.requireNonNull(shortcut);
@@ -2007,7 +2105,8 @@
ps.updateInvisibleShortcutForPinRequestWith(shortcut);
- packageShortcutsChanged(shortcutPackage, userId);
+ packageShortcutsChanged(shortcutPackage, userId,
+ Collections.singletonList(shortcut), null);
}
}
@@ -2052,7 +2151,8 @@
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId);
+ // TODO: Disabling dynamic shortcuts will removed them if not pinned. Cover all cases.
+ packageShortcutsChanged(packageName, userId, null, null);
verifyStates();
}
@@ -2062,6 +2162,8 @@
verifyCaller(packageName, userId);
Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
+ List<ShortcutInfo> changedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2076,9 +2178,18 @@
continue;
}
ps.enableWithId(id);
+
+ final ShortcutInfo si = ps.findShortcutById(id);
+ if (si != null) {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
+ }
}
}
- packageShortcutsChanged(packageName, userId);
+
+ packageShortcutsChanged(packageName, userId, changedShortcuts, null);
verifyStates();
}
@@ -2089,6 +2200,9 @@
verifyCaller(packageName, userId);
Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
+ List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2102,13 +2216,25 @@
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
continue;
}
- ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+ final ShortcutInfo si = ps.findShortcutById(id);
+ final boolean removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+ if (removed) {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(si);
+ } else {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
+ }
}
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -2117,13 +2243,19 @@
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
+ List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
- ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+
+ removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
}
- packageShortcutsChanged(packageName, userId);
+
+ // TODO: Pinned and cached shortcuts are not removed, add those to changedShortcuts list
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -2134,6 +2266,9 @@
verifyCaller(packageName, userId);
Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
+ List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2144,13 +2279,29 @@
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
- ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+
+ final ShortcutInfo si = ps.findShortcutById(id);
+ final boolean removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+
+ if (si != null) {
+ if (removed) {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(si);
+ } else {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
+ }
+ }
}
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -2742,6 +2893,8 @@
Preconditions.checkStringNotEmpty(packageName, "packageName");
Objects.requireNonNull(shortcutIds, "shortcutIds");
+ List<ShortcutInfo> changedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
@@ -2751,8 +2904,23 @@
launcher.attemptToRestoreIfNeededAndSave();
launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
+
+ final ShortcutPackage sp = getUserShortcutsLocked(userId)
+ .getPackageShortcutsIfExists(packageName);
+ if (sp != null) {
+ for (int i = 0; i < shortcutIds.size(); i++) {
+ final ShortcutInfo si = sp.findShortcutById(shortcutIds.get(i));
+ if (si != null) {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
+ }
+ }
+ }
}
- packageShortcutsChanged(packageName, userId);
+ // TODO: Include previously pinned shortcuts since they are not pinned anymore.
+ packageShortcutsChanged(packageName, userId, changedShortcuts, null);
verifyStates();
}
@@ -2787,6 +2955,9 @@
Preconditions.checkStringNotEmpty(packageName, "packageName");
Objects.requireNonNull(shortcutIds, "shortcutIds");
+ List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
throwIfUserLockedL(launcherUserId);
@@ -2808,20 +2979,36 @@
if (doCache) {
if (si.isDynamic() && si.isLongLived()) {
si.addFlags(ShortcutInfo.FLAG_CACHED);
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
} else {
Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring"
+ "shortcut " + si.getId());
}
} else {
+ boolean removed = false;
if (si.isDynamic()) {
si.clearFlags(ShortcutInfo.FLAG_CACHED);
} else {
- sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+ removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+ }
+ if (removed) {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(si);
+ } else {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(si);
}
}
}
}
- packageShortcutsChanged(packageName, userId);
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -2867,6 +3054,14 @@
}
@Override
+ public void addShortcutChangeCallback(
+ @NonNull LauncherApps.ShortcutChangeCallback callback) {
+ synchronized (mLock) {
+ mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
+ }
+ }
+
+ @Override
public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
Objects.requireNonNull(callingPackage, "callingPackage");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 614cc3f..fe99229 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -39,7 +40,6 @@
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.ParceledListSlice;
-import android.content.pm.parsing.AndroidPackage;
import android.content.rollback.IRollbackManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
@@ -68,6 +68,8 @@
import com.android.internal.content.PackageHelper;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.rollback.WatchdogRollbackLogger;
import java.io.File;
@@ -284,8 +286,10 @@
throws PackageManagerException {
final long activeVersion = activePackage.applicationInfo.longVersionCode;
final long newVersionCode = newPackage.applicationInfo.longVersionCode;
+ boolean isAppDebuggable = (activePackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
- session.params.installFlags, activePackage.applicationInfo.flags);
+ session.params.installFlags, isAppDebuggable);
if (activeVersion > newVersionCode && !allowsDowngrade) {
if (!mApexManager.abortStagedSession(session.sessionId)) {
Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
@@ -426,11 +430,10 @@
+ "for snapshotting/restoring user data.");
return;
}
- final String seInfo = pkg.getSeInfo();
int appId = -1;
long ceDataInode = -1;
- final PackageSetting ps = (PackageSetting) mPmi.getPackageSetting(packageName);
+ final PackageSetting ps = mPmi.getPackageSetting(packageName);
if (ps != null) {
appId = ps.appId;
ceDataInode = ps.getCeDataInode(UserHandle.USER_SYSTEM);
@@ -438,6 +441,7 @@
// an update, and hence need to restore data for all installed users.
final int[] installedUsers = ps.queryInstalledUsers(allUsers, true);
+ final String seInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
try {
rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
seInfo, 0 /*token*/);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 67b1008..27e8b0b 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -585,6 +585,9 @@
android.provider.Settings.Global.putStringForUser(cr,
android.provider.Settings.Global.ADB_ENABLED, "0",
userId);
+ android.provider.Settings.Global.putStringForUser(cr,
+ android.provider.Settings.Global.ADB_WIFI_ENABLED, "0",
+ userId);
}
}
break;
@@ -721,6 +724,7 @@
break;
case android.provider.Settings.Global.ADB_ENABLED:
+ case android.provider.Settings.Global.ADB_WIFI_ENABLED:
if ("0".equals(value)) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 4c40448..d6480d3 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -22,7 +22,6 @@
import android.annotation.UserIdInt;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.AndroidPackage;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -33,6 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -190,13 +190,14 @@
// Install/uninstall system packages per user.
for (int userId : mUm.getUserIds()) {
final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
- pmInt.forEachPackage(pkg -> {
- if (!pkg.isSystem()) {
+ pmInt.forEachPackageSetting(pkgSetting -> {
+ AndroidPackage pkg = pkgSetting.pkg;
+ if (pkg == null || !pkg.isSystem()) {
return;
}
final boolean install =
(userWhitelist == null || userWhitelist.contains(pkg.getPackageName()))
- && !pkg.isHiddenUntilInstalled();
+ && !pkgSetting.getPkgState().isHiddenUntilInstalled();
if (isConsideredUpgrade && !isFirstBoot && !install) {
return; // To be careful, we don’t uninstall apps during OTAs
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 0caab6d..51e07faf 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -31,7 +31,7 @@
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.content.pm.dex.PackageOptimizationInfo;
-import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -54,6 +54,7 @@
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -482,8 +483,10 @@
try {
final String packageName = pkg.getPackageName();
final String apkPath = pkg.getBaseCodePath();
- final String outDexFile = pkg.getDataDir() + "/code_cache/compiled_view.dex";
- if (pkg.isPrivileged() || pkg.isEmbeddedDexUsed()
+ // TODO(b/143971007): Use a cross-user directory
+ File dataDir = PackageInfoWithoutStateUtils.getDataDir(pkg, UserHandle.myUserId());
+ final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
+ if (pkg.isPrivileged() || pkg.isUseEmbeddedDex()
|| pkg.isDefaultToDeviceProtectedStorage()) {
// Privileged apps prefer to load trusted code so they don't use compiled views.
// If the app is not privileged but prefers code integrity, also avoid compiling
@@ -516,7 +519,7 @@
*/
private ArrayMap<String, String> getPackageProfileNames(AndroidPackage pkg) {
ArrayMap<String, String> result = new ArrayMap<>();
- if ((pkg.getFlags() & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ if (pkg.isHasCode()) {
result.put(pkg.getBaseCodePath(), ArtManager.getProfileName(null));
}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index df24c013..117cc5e 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -41,6 +41,8 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerServiceUtils;
+import dalvik.system.VMRuntime;
+
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -136,22 +138,15 @@
* return as fast as possible.
*
* @param loadingAppInfo the package performing the load
- * @param classLoadersNames the names of the class loaders present in the loading chain. The
- * list encodes the class loader chain in the natural order. The first class loader has
- * the second one as its parent and so on. The dex files present in the class path of the
- * first class loader will be recorded in the usage file.
- * @param classPaths the class paths corresponding to the class loaders names from
- * {@param classLoadersNames}. The the first element corresponds to the first class loader
- * and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}, or null if the class loader's classpath is not known.
- * The dex files found in the first class path will be recorded in the usage file.
+ * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+ * the class loader context that was used to load them.
* @param loaderIsa the ISA of the app loading the dex files
* @param loaderUserId the user id which runs the code loading the dex files
*/
- public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
- List<String> classPaths, String loaderIsa, int loaderUserId) {
+ public void notifyDexLoad(ApplicationInfo loadingAppInfo,
+ Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
try {
- notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
+ notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
loaderUserId);
} catch (Exception e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
@@ -161,46 +156,23 @@
@VisibleForTesting
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
- List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
+ Map<String, String> classLoaderContextMap, String loaderIsa,
int loaderUserId) {
- if (classLoaderNames.size() != classPaths.size()) {
- Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
+ if (classLoaderContextMap == null) {
return;
}
- if (classLoaderNames.isEmpty()) {
+ if (classLoaderContextMap.isEmpty()) {
Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
return;
}
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
- Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
- loaderIsa + "?");
+ Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet()
+ + " in unsupported ISA: " + loaderIsa + "?");
return;
}
- // The first classpath should never be null because the first classloader
- // should always be an instance of BaseDexClassLoader.
- String firstClassPath = classPaths.get(0);
- if (firstClassPath == null) {
- return;
- }
- // The classpath is represented as a list of dex files separated by File.pathSeparator.
- String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator);
-
- // Encode the class loader contexts for the dexPathsToRegister.
- String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
- classLoaderNames, classPaths);
-
- // A null classLoaderContexts means that there are unsupported class loaders in the
- // chain.
- if (classLoaderContexts == null) {
- if (DEBUG) {
- Slog.i(TAG, loadingAppInfo.packageName +
- " uses unsupported class loader in " + classLoaderNames);
- }
- }
-
- int dexPathIndex = 0;
- for (String dexPath : dexPathsToRegister) {
+ for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
+ String dexPath = mapping.getKey();
// Find the owning package name.
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
@@ -222,7 +194,6 @@
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
// do not record it. This case does not bring any new usable information
// and can be safely skipped.
- dexPathIndex++;
continue;
}
@@ -232,13 +203,13 @@
searchResult.mOwningPackageName, loadingAppInfo.packageName);
}
- if (classLoaderContexts != null) {
-
+ String classLoaderContext = mapping.getValue();
+ if (classLoaderContext != null
+ && VMRuntime.isValidClassLoaderContext(classLoaderContext)) {
// Record dex file usage. If the current usage is a new pattern (e.g. new
// secondary, or UsedByOtherApps), record will return true and we trigger an
// async write to disk to make sure we don't loose the data in case of a reboot.
- String classLoaderContext = classLoaderContexts[dexPathIndex];
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
loadingAppInfo.packageName, classLoaderContext)) {
@@ -252,7 +223,6 @@
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
}
}
- dexPathIndex++;
}
}
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 6e6b137..6807388 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -18,7 +18,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import android.util.Slog;
import android.util.SparseArray;
@@ -77,7 +77,7 @@
}
String baseApkContextClassLoader = encodeClassLoader(
- "", pkg.getAppInfoClassLoaderName(), sharedLibrariesContext);
+ "", pkg.getClassLoaderName(), sharedLibrariesContext);
if (pkg.getSplitCodePaths() == null) {
// The application has no splits.
return new String[] {baseApkContextClassLoader};
@@ -101,7 +101,7 @@
SparseArray<int[]> splitDependencies = pkg.getSplitDependencies();
- if (!pkg.requestsIsolatedSplitLoading()
+ if (!pkg.isIsolatedSplitLoading()
|| splitDependencies == null
|| splitDependencies.size() == 0) {
// If the app didn't request for the splits to be loaded in isolation or if it does not
@@ -111,7 +111,7 @@
for (int i = 1; i < classLoaderContexts.length; i++) {
if (pathsWithCode[i]) {
classLoaderContexts[i] = encodeClassLoader(
- classpath, pkg.getAppInfoClassLoaderName(), sharedLibrariesContext);
+ classpath, pkg.getClassLoaderName(), sharedLibrariesContext);
} else {
classLoaderContexts[i] = null;
}
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index e68c238..08763e7 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -83,8 +83,9 @@
"=UnknownClassLoaderContext=";
// The marker used for unsupported class loader contexts (no longer written, may occur in old
- // files so discarded on read).
- private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+ // files so discarded on read). Note: this matches
+ // ClassLoaderContext::kUnsupportedClassLoaderContextEncoding in the runtime.
+ /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
"=UnsupportedClassLoaderContext=";
/**
@@ -133,6 +134,9 @@
if (classLoaderContext == null) {
throw new IllegalArgumentException("Null classLoaderContext");
}
+ if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
+ return false;
+ }
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
@@ -843,10 +847,11 @@
boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
String oldClassLoaderContext = mClassLoaderContext;
- if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
+ if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
// Can happen if we read a previous version.
mClassLoaderContext = dexUseInfo.mClassLoaderContext;
- } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+ } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
+ && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
// We detected a context change.
mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
}
@@ -857,6 +862,13 @@
|| !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
}
+ private static boolean isUnknownOrUnsupportedContext(String context) {
+ // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
+ // into UNSUPPORTED_CLASS_LOADER_CONTEXT.
+ return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
+ || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
+ }
+
public boolean isUsedByOtherApps() {
return mIsUsedByOtherApps;
}
@@ -878,7 +890,7 @@
public boolean isUnknownClassLoaderContext() {
// The class loader context may be unknown if we loaded the data from a previous version
// which didn't save the context.
- return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+ return isUnknownOrUnsupportedContext(mClassLoaderContext);
}
public boolean isVariableClassLoaderContext() {
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index b7443f3..5506a52 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -16,12 +16,16 @@
package com.android.server.pm.dex;
-import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.os.Binder;
+import android.os.UserHandle;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
+import java.io.File;
public class ViewCompiler {
private final Object mInstallLock;
@@ -37,7 +41,9 @@
try {
final String packageName = pkg.getPackageName();
final String apkPath = pkg.getBaseCodePath();
- final String outDexFile = pkg.getDataDir() + "/code_cache/compiled_view.dex";
+ // TODO(b/143971007): Use a cross-user directory
+ File dataDir = PackageInfoWithoutStateUtils.getDataDir(pkg, UserHandle.myUserId());
+ final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
long callingId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
new file mode 100644
index 0000000..e5e1b0b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageParserCacheHelper;
+import android.os.Parcel;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class PackageCacher {
+
+ private static final String TAG = "PackageCacher";
+
+ /**
+ * Total number of packages that were read from the cache. We use it only for logging.
+ */
+ public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();
+
+ @NonNull
+ private File mCacheDir;
+
+ public PackageCacher(@NonNull File cacheDir) {
+ this.mCacheDir = cacheDir;
+ }
+
+ /**
+ * Returns the cache key for a specified {@code packageFile} and {@code flags}.
+ */
+ private String getCacheKey(File packageFile, int flags) {
+ StringBuilder sb = new StringBuilder(packageFile.getName());
+ sb.append('-');
+ sb.append(flags);
+
+ return sb.toString();
+ }
+
+ @VisibleForTesting
+ protected ParsedPackage fromCacheEntry(byte[] bytes) {
+ return fromCacheEntryStatic(bytes);
+ }
+
+ /** static version of {@link #fromCacheEntry} for unit tests. */
+ @VisibleForTesting
+ public static ParsedPackage fromCacheEntryStatic(byte[] bytes) {
+ final Parcel p = Parcel.obtain();
+ p.unmarshall(bytes, 0, bytes.length);
+ p.setDataPosition(0);
+
+ final PackageParserCacheHelper.ReadHelper helper = new PackageParserCacheHelper.ReadHelper(p);
+ helper.startAndInstall();
+
+ // TODO(b/135203078): Hide PackageImpl constructor?
+ ParsedPackage pkg = new PackageImpl(p);
+
+ p.recycle();
+
+ sCachedPackageReadCount.incrementAndGet();
+
+ return pkg;
+ }
+
+ @VisibleForTesting
+ protected byte[] toCacheEntry(ParsedPackage pkg) {
+ return toCacheEntryStatic(pkg);
+
+ }
+
+ /** static version of {@link #toCacheEntry} for unit tests. */
+ @VisibleForTesting
+ public static byte[] toCacheEntryStatic(ParsedPackage pkg) {
+ final Parcel p = Parcel.obtain();
+ final PackageParserCacheHelper.WriteHelper helper = new PackageParserCacheHelper.WriteHelper(p);
+
+ pkg.writeToParcel(p, 0 /* flags */);
+
+ helper.finishAndUninstall();
+
+ byte[] serialized = p.marshall();
+ p.recycle();
+
+ return serialized;
+ }
+
+ /**
+ * Given a {@code packageFile} and a {@code cacheFile} returns whether the
+ * cache file is up to date based on the mod-time of both files.
+ */
+ private static boolean isCacheUpToDate(File packageFile, File cacheFile) {
+ try {
+ // NOTE: We don't use the File.lastModified API because it has the very
+ // non-ideal failure mode of returning 0 with no excepions thrown.
+ // The nio2 Files API is a little better but is considerably more expensive.
+ final StructStat pkg = Os.stat(packageFile.getAbsolutePath());
+ final StructStat cache = Os.stat(cacheFile.getAbsolutePath());
+ return pkg.st_mtime < cache.st_mtime;
+ } catch (ErrnoException ee) {
+ // The most common reason why stat fails is that a given cache file doesn't
+ // exist. We ignore that here. It's easy to reason that it's safe to say the
+ // cache isn't up to date if we see any sort of exception here.
+ //
+ // (1) Exception while stating the package file : This should never happen,
+ // and if it does, we do a full package parse (which is likely to throw the
+ // same exception).
+ // (2) Exception while stating the cache file : If the file doesn't exist, the
+ // cache is obviously out of date. If the file *does* exist, we can't read it.
+ // We will attempt to delete and recreate it after parsing the package.
+ if (ee.errno != OsConstants.ENOENT) {
+ Slog.w("Error while stating package cache : ", ee);
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Returns the cached parse result for {@code packageFile} for parse flags {@code flags},
+ * or {@code null} if no cached result exists.
+ */
+ public ParsedPackage getCachedResult(File packageFile, int flags) {
+ final String cacheKey = getCacheKey(packageFile, flags);
+ final File cacheFile = new File(mCacheDir, cacheKey);
+
+ try {
+ // If the cache is not up to date, return null.
+ if (!isCacheUpToDate(packageFile, cacheFile)) {
+ return null;
+ }
+
+ final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
+ return fromCacheEntry(bytes);
+ } catch (Throwable e) {
+ Slog.w(TAG, "Error reading package cache: ", e);
+
+ // If something went wrong while reading the cache entry, delete the cache file
+ // so that we regenerate it the next time.
+ cacheFile.delete();
+ return null;
+ }
+ }
+
+ /**
+ * Caches the parse result for {@code packageFile} with flags {@code flags}.
+ */
+ public void cacheResult(File packageFile, int flags, ParsedPackage parsed) {
+ try {
+ final String cacheKey = getCacheKey(packageFile, flags);
+ final File cacheFile = new File(mCacheDir, cacheKey);
+
+ if (cacheFile.exists()) {
+ if (!cacheFile.delete()) {
+ Slog.e(TAG, "Unable to delete cache file: " + cacheFile);
+ }
+ }
+
+ final byte[] cacheEntry = toCacheEntry(parsed);
+
+ if (cacheEntry == null) {
+ return;
+ }
+
+ try (FileOutputStream fos = new FileOutputStream(cacheFile)) {
+ fos.write(cacheEntry);
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Error writing cache entry.", ioe);
+ cacheFile.delete();
+ }
+ } catch (Throwable e) {
+ Slog.w(TAG, "Error saving package cache.", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
new file mode 100644
index 0000000..23bdf5f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing;
+
+import android.annotation.CheckResult;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apex.ApexInfo;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageUserState;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProcessInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.component.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProcess;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.pkg.PackageStateUnserialized;
+
+import libcore.util.EmptyArray;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Methods that use a {@link PackageSetting} use it to override information provided from the raw
+ * package, or to provide information that would otherwise be missing. Null can be passed if none
+ * of the state values should be applied.
+ *
+ * @hide
+ **/
+public class PackageInfoUtils {
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static PackageInfo generate(AndroidPackage pkg, int[] gids,
+ @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ Set<String> grantedPermissions, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime,
+ grantedPermissions, state, userId, null, pkgSetting);
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static PackageInfo generate(AndroidPackage pkg, ApexInfo apexInfo, int flags,
+ @Nullable PackageSetting pkgSetting) {
+ return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(),
+ new PackageUserState(), UserHandle.getCallingUserId(), apexInfo, pkgSetting);
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ private static PackageInfo generateWithComponents(AndroidPackage pkg, int[] gids,
+ @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime,
+ Set<String> grantedPermissions, PackageUserState state, int userId,
+ @Nullable ApexInfo apexInfo, @Nullable PackageSetting pkgSetting) {
+ ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId,
+ pkgSetting);
+ if (applicationInfo == null) {
+ return null;
+ }
+
+ PackageInfo info = PackageInfoWithoutStateUtils.generateWithoutComponents(pkg, gids, flags,
+ firstInstallTime, lastUpdateTime, grantedPermissions, state, userId, apexInfo,
+ applicationInfo);
+ if (info == null) {
+ return null;
+ }
+
+ info.isStub = pkg.isStub();
+ info.coreApp = pkg.isCoreApp();
+
+ if ((flags & PackageManager.GET_ACTIVITIES) != 0) {
+ final int N = pkg.getActivities().size();
+ if (N > 0) {
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[N];
+ for (int i = 0; i < N; i++) {
+ final ParsedActivity a = pkg.getActivities().get(i);
+ if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), a,
+ flags)) {
+ if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
+ a.getName())) {
+ continue;
+ }
+ res[num++] = generateActivityInfo(pkg, a, flags, state,
+ applicationInfo, userId, pkgSetting);
+ }
+ }
+ info.activities = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_RECEIVERS) != 0) {
+ final int size = pkg.getReceivers().size();
+ if (size > 0) {
+ int num = 0;
+ final ActivityInfo[] res = new ActivityInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedActivity a = pkg.getReceivers().get(i);
+ if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), a,
+ flags)) {
+ res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
+ userId, pkgSetting);
+ }
+ }
+ info.receivers = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_SERVICES) != 0) {
+ final int size = pkg.getServices().size();
+ if (size > 0) {
+ int num = 0;
+ final ServiceInfo[] res = new ServiceInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedService s = pkg.getServices().get(i);
+ if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), s,
+ flags)) {
+ res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo,
+ userId, pkgSetting);
+ }
+ }
+ info.services = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_PROVIDERS) != 0) {
+ final int size = pkg.getProviders().size();
+ if (size > 0) {
+ int num = 0;
+ final ProviderInfo[] res = new ProviderInfo[size];
+ for (int i = 0; i < size; i++) {
+ final ParsedProvider pr = pkg.getProviders()
+ .get(i);
+ if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), pr,
+ flags)) {
+ res[num++] = generateProviderInfo(pkg, pr, flags, state, applicationInfo,
+ userId, pkgSetting);
+ }
+ }
+ info.providers = ArrayUtils.trimToSize(res, num);
+ }
+ }
+ if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) {
+ int N = pkg.getInstrumentations().size();
+ if (N > 0) {
+ info.instrumentation = new InstrumentationInfo[N];
+ for (int i = 0; i < N; i++) {
+ info.instrumentation[i] = generateInstrumentationInfo(
+ pkg.getInstrumentations().get(i), pkg, flags, userId, pkgSetting);
+ }
+ }
+ }
+
+ return info;
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static ApplicationInfo generateApplicationInfo(AndroidPackage pkg,
+ @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ // TODO(b/135203078): Consider cases where we don't have a PkgSetting
+ if (pkg == null) {
+ return null;
+ }
+
+ if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)
+ || !AndroidPackageUtils.isMatchForSystemOnly(pkg, flags)) {
+ return null;
+ }
+
+ ApplicationInfo info = PackageInfoWithoutStateUtils.generateApplicationInfo(pkg, flags,
+ state, userId);
+ if (info == null) {
+ return null;
+ }
+
+ if (pkgSetting != null) {
+ // TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
+ PackageStateUnserialized pkgState = pkgSetting.getPkgState();
+ info.hiddenUntilInstalled = pkgState.isHiddenUntilInstalled();
+ List<String> usesLibraryFiles = pkgState.getUsesLibraryFiles();
+ List<SharedLibraryInfo> usesLibraryInfos = pkgState.getUsesLibraryInfos();
+ info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
+ ? null : usesLibraryFiles.toArray(new String[0]);
+ info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
+ }
+
+ info.flags |= appInfoFlags(pkg, pkgSetting);
+ info.privateFlags |= appInfoPrivateFlags(pkg, pkgSetting);
+ return info;
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ return generateActivityInfo(pkg, a, flags, state, null, userId, pkgSetting);
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ private static ActivityInfo generateActivityInfo(AndroidPackage pkg, ParsedActivity a,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @Nullable ApplicationInfo applicationInfo, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ if (a == null) return null;
+ if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
+ return null;
+ }
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
+ }
+ ActivityInfo info = PackageInfoWithoutStateUtils.generateActivityInfo(pkg, a, flags, state,
+ applicationInfo, userId);
+ if (info == null) {
+ return null;
+ }
+
+ assignSharedFieldsForComponentInfo(info, a, pkgSetting);
+ return info;
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ return generateServiceInfo(pkg, s, flags, state, null, userId, pkgSetting);
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ private static ServiceInfo generateServiceInfo(AndroidPackage pkg, ParsedService s,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @Nullable ApplicationInfo applicationInfo, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ if (s == null) return null;
+ if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
+ return null;
+ }
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
+ }
+ ServiceInfo info = PackageInfoWithoutStateUtils.generateServiceInfo(pkg, s, flags, state,
+ applicationInfo, userId);
+ if (info == null) {
+ return null;
+ }
+
+ assignSharedFieldsForComponentInfo(info, s, pkgSetting);
+ return info;
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ return generateProviderInfo(pkg, p, flags, state, null, userId, pkgSetting);
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
+ @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
+ @Nullable ApplicationInfo applicationInfo, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ if (p == null) return null;
+ if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
+ return null;
+ }
+ if (applicationInfo == null) {
+ applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
+ }
+ ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfo(pkg, p, flags, state,
+ applicationInfo, userId);
+ if (info == null) {
+ return null;
+ }
+
+ assignSharedFieldsForComponentInfo(info, p, pkgSetting);
+ return info;
+ }
+
+ /**
+ * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
+ */
+ @Nullable
+ public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i,
+ AndroidPackage pkg, @PackageManager.ComponentInfoFlags int flags, int userId,
+ @Nullable PackageSetting pkgSetting) {
+ if (i == null) return null;
+
+ InstrumentationInfo info =
+ PackageInfoWithoutStateUtils.generateInstrumentationInfo(i, pkg, flags, userId);
+ if (info == null) {
+ return null;
+ }
+
+ // TODO(b/135203078): Add setting related state
+ info.primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
+ info.secondaryCpuAbi = AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting);
+ info.nativeLibraryDir = pkg.getNativeLibraryDir();
+ info.secondaryNativeLibraryDir = pkg.getSecondaryNativeLibraryDir();
+
+ assignStateFieldsForPackageItemInfo(info, i, pkgSetting);
+
+ return info;
+ }
+
+ // TODO(b/135203078): Determine if permission methods need to pass in a non-null PackageSetting
+ // os that checkUseInstalledOrHidden filter can apply
+ @Nullable
+ public static PermissionInfo generatePermissionInfo(ParsedPermission p,
+ @PackageManager.ComponentInfoFlags int flags) {
+ // TODO(b/135203078): Remove null checks and make all usages @NonNull
+ if (p == null) return null;
+
+ // For now, permissions don't have state-adjustable fields; return directly
+ return PackageInfoWithoutStateUtils.generatePermissionInfo(p, flags);
+ }
+
+ @Nullable
+ public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg,
+ @PackageManager.ComponentInfoFlags int flags) {
+ if (pg == null) return null;
+
+ // For now, permissions don't have state-adjustable fields; return directly
+ return PackageInfoWithoutStateUtils.generatePermissionGroupInfo(pg, flags);
+ }
+
+ @Nullable
+ public static ArrayMap<String, ProcessInfo> generateProcessInfo(
+ Map<String, ParsedProcess> procs, @PackageManager.ComponentInfoFlags int flags) {
+ if (procs == null) {
+ return null;
+ }
+
+ final int numProcs = procs.size();
+ ArrayMap<String, ProcessInfo> retProcs = new ArrayMap<>(numProcs);
+ for (String key : procs.keySet()) {
+ ParsedProcess proc = procs.get(key);
+ retProcs.put(proc.getName(), new ProcessInfo(proc.getName(),
+ new ArraySet<>(proc.getDeniedPermissions())));
+ }
+ return retProcs;
+ }
+
+ /**
+ * Returns true if the package is installed and not hidden, or if the caller
+ * explicitly wanted all uninstalled and hidden packages as well.
+ */
+ private static boolean checkUseInstalledOrHidden(AndroidPackage pkg,
+ PackageSetting pkgSetting, PackageUserState state,
+ @PackageManager.PackageInfoFlags int flags) {
+ // Returns false if the package is hidden system app until installed.
+ if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+ && !state.installed
+ && pkgSetting != null
+ && pkgSetting.getPkgState().isHiddenUntilInstalled()) {
+ return false;
+ }
+
+ // If available for the target user, or trying to match uninstalled packages and it's
+ // a system app.
+ return state.isAvailable(flags)
+ || (pkg.isSystem()
+ && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+ || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
+ }
+
+ private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
+ @NonNull ParsedMainComponent mainComponent, @Nullable PackageSetting pkgSetting) {
+ assignStateFieldsForPackageItemInfo(componentInfo, mainComponent, pkgSetting);
+ componentInfo.descriptionRes = mainComponent.getDescriptionRes();
+ componentInfo.directBootAware = mainComponent.isDirectBootAware();
+ componentInfo.enabled = mainComponent.isEnabled();
+ componentInfo.splitName = mainComponent.getSplitName();
+ }
+
+ private static void assignStateFieldsForPackageItemInfo(
+ @NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component,
+ @Nullable PackageSetting pkgSetting) {
+ // TODO(b/135203078): Add setting related state
+ }
+
+ @CheckResult
+ private static int flag(boolean hasFlag, int flag) {
+ return hasFlag ? flag : 0;
+ }
+
+ /** @see ApplicationInfo#flags */
+ public static int appInfoFlags(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ // TODO(b/135203078): Add setting related state
+ // @formatter:off
+ int flags = PackageInfoWithoutStateUtils.appInfoFlags(pkg)
+ | flag(pkg.isSystem(), ApplicationInfo.FLAG_SYSTEM)
+ | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST);
+ if (pkgSetting != null) {
+ flags |= flag(pkgSetting.getPkgState().isUpdatedSystemApp(), ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+ }
+ return flags;
+ // @formatter:on
+ }
+
+ /** @see ApplicationInfo#privateFlags */
+ public static int appInfoPrivateFlags(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ // TODO(b/135203078): Add setting related state
+ // @formatter:off
+ return PackageInfoWithoutStateUtils.appInfoPrivateFlags(pkg)
+ | flag(pkg.isSystemExt(), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
+ | flag(pkg.isPrivileged(), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+ | flag(pkg.isOem(), ApplicationInfo.PRIVATE_FLAG_OEM)
+ | flag(pkg.isVendor(), ApplicationInfo.PRIVATE_FLAG_VENDOR)
+ | flag(pkg.isProduct(), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
+ | flag(pkg.isOdm(), ApplicationInfo.PRIVATE_FLAG_ODM)
+ | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
+ // @formatter:on
+ }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
new file mode 100644
index 0000000..f99791a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing;
+
+import android.annotation.AnyThread;
+import android.annotation.Nullable;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import java.io.File;
+
+/**
+ * The v2 of {@link PackageParser} for use when parsing is initiated in the server and must
+ * contain state contained by the server.
+ */
+public class PackageParser2 {
+
+ private static final String TAG = "PackageParser2";
+
+ private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
+ private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;
+
+ private ThreadLocal<ParseTypeImpl> mSharedResult = ThreadLocal.withInitial(ParseTypeImpl::new);
+
+ private final String[] mSeparateProcesses;
+ private final boolean mOnlyCoreApps;
+ private final DisplayMetrics mDisplayMetrics;
+
+ @Nullable
+ protected PackageCacher mCacher;
+
+ private ParsingPackageUtils parsingUtils;
+
+ /**
+ * @param onlyCoreApps Flag indicating this parser should only consider apps with
+ * {@code coreApp} manifest attribute to be valid apps. This is useful when
+ * creating a minimalist boot environment.
+ */
+ public PackageParser2(String[] separateProcesses, boolean onlyCoreApps,
+ DisplayMetrics displayMetrics, @Nullable File cacheDir, Callback callback) {
+ mSeparateProcesses = separateProcesses;
+ mOnlyCoreApps = onlyCoreApps;
+
+ if (displayMetrics == null) {
+ mDisplayMetrics = new DisplayMetrics();
+ mDisplayMetrics.setToDefaults();
+ } else {
+ mDisplayMetrics = displayMetrics;
+ }
+
+ mCacher = cacheDir == null ? null : new PackageCacher(cacheDir);
+ // TODO(b/135203078): Remove nullability of callback
+ callback = callback != null ? callback : new Callback() {
+ @Override
+ public boolean hasFeature(String feature) {
+ return false;
+ }
+ };
+
+ parsingUtils = new ParsingPackageUtils(onlyCoreApps, separateProcesses, displayMetrics, callback);
+ }
+
+ /**
+ * TODO(b/135203078): Document new package parsing
+ */
+ @AnyThread
+ public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
+ throws PackageParserException {
+ if (useCaches && mCacher != null) {
+ ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
+ if (parsed != null) {
+ return parsed;
+ }
+ }
+
+ long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
+ ParseInput input = mSharedResult.get().reset();
+ ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
+ if (result.isError()) {
+ throw new PackageParserException(result.getErrorCode(), result.getErrorMessage(),
+ result.getException());
+ }
+
+ ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
+
+ long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
+ if (mCacher != null) {
+ mCacher.cacheResult(packageFile, flags, parsed);
+ }
+ if (LOG_PARSE_TIMINGS) {
+ parseTime = cacheTime - parseTime;
+ cacheTime = SystemClock.uptimeMillis() - cacheTime;
+ if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
+ Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
+ + "ms, update_cache=" + cacheTime + " ms");
+ }
+ }
+
+ return parsed;
+ }
+
+ public static abstract class Callback implements ParsingPackageUtils.Callback {
+
+ @Override
+ public final ParsingPackage startParsingPackage(String packageName, String baseCodePath,
+ String codePath, TypedArray manifestArray, boolean isCoreApp) {
+ return PackageImpl.forParsing(packageName, baseCodePath, codePath, manifestArray,
+ isCoreApp);
+ }
+ }
+}
diff --git a/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
similarity index 71%
rename from core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java
rename to services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
index 81b4bc5..ebb96bb 100644
--- a/core/java/android/content/pm/parsing/library/AndroidHidlUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,31 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
/**
* Updates a package to ensure that if it targets <= P that the android.hidl.base-V1.0-java
* and android.hidl.manager-V1.0-java libraries are included by default.
*
+ * TODO(b/135203078): See if this class can be removed, thus removing the isUpdatedSystemApp param
+ *
* @hide
*/
@VisibleForTesting
public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
@Override
- public void updatePackage(ParsedPackage parsedPackage) {
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
// This was the default <= P and is maintained for backwards compatibility.
boolean isLegacy = parsedPackage.getTargetSdkVersion() <= Build.VERSION_CODES.P;
// Only system apps use these libraries
- boolean isSystem = parsedPackage.isSystemApp() || parsedPackage.isUpdatedSystemApp();
+ boolean isSystem = parsedPackage.isSystem() || isUpdatedSystemApp;
if (isLegacy && isSystem) {
prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_BASE);
diff --git a/core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
similarity index 87%
rename from core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java
rename to services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
index 5fbe5b9..432394a 100644
--- a/core/java/android/content/pm/parsing/library/AndroidTestBaseUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,16 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.Context;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,6 +28,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.compat.IPlatformCompat;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
/**
* Updates a package to ensure that if it targets <= Q that the android.test.base library is
@@ -73,7 +73,7 @@
}
@Override
- public void updatePackage(ParsedPackage pkg) {
+ public void updatePackage(ParsedPackage pkg, boolean isUpdatedSystemApp) {
// Packages targeted at <= Q 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.
diff --git a/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
similarity index 78%
rename from core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
rename to services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
index 613a06b..7de457e 100644
--- a/core/java/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
/**
* Updates a package to ensure that if it targets < P that the org.apache.http.legacy library is
@@ -37,7 +37,7 @@
}
@Override
- public void updatePackage(ParsedPackage parsedPackage) {
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
// 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.
diff --git a/core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
similarity index 62%
rename from core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java
rename to services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 1220fc4..1d018c4 100644
--- a/core/java/android/content/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
-import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.PackageParser;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Supplier;
/**
* Modifies {@link ParsedPackage} in order to maintain backwards compatibility.
@@ -55,13 +55,7 @@
// 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.parsing.library.AndroidTestBaseUpdater",
- RemoveUnnecessaryAndroidTestBaseLibrary::new);
+ boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters);
PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
.toArray(new PackageSharedLibraryUpdater[0]);
@@ -70,41 +64,31 @@
}
/**
- * Add an optional {@link PackageSharedLibraryUpdater} instance to the list, if it could not be
- * found then add a default instance instead.
+ * 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.
*
- * @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.
+ * TODO:(b/135203078): Find a better way to do this.
*/
- private static boolean addOptionalUpdater(List<PackageSharedLibraryUpdater> packageUpdaters,
- String className, Supplier<PackageSharedLibraryUpdater> defaultUpdater) {
- Class<? extends PackageSharedLibraryUpdater> clazz;
+ private static boolean addUpdaterForAndroidTestBase(
+ List<PackageSharedLibraryUpdater> packageUpdaters) {
+ boolean hasClass = false;
+ String className = "android.content.pm.AndroidTestBaseUpdater";
try {
- clazz = (PackageBackwardCompatibility.class.getClassLoader()
- .loadClass(className)
- .asSubclass(PackageSharedLibraryUpdater.class));
+ Class clazz = (PackageParser.class.getClassLoader().loadClass(className));
+ hasClass = clazz != null;
Log.i(TAG, "Loaded " + className);
} catch (ClassNotFoundException e) {
Log.i(TAG, "Could not find " + className + ", ignoring");
- clazz = null;
}
- boolean usedOptional = false;
- PackageSharedLibraryUpdater updater;
- if (clazz == null) {
- updater = defaultUpdater.get();
+ if (hasClass) {
+ packageUpdaters.add(new AndroidTestBaseUpdater());
} else {
- try {
- updater = clazz.getConstructor().newInstance();
- usedOptional = true;
- } catch (ReflectiveOperationException e) {
- throw new IllegalStateException("Could not create instance of " + className, e);
- }
+ packageUpdaters.add(new RemoveUnnecessaryAndroidTestBaseLibrary());
}
- packageUpdaters.add(updater);
- return usedOptional;
+ return hasClass;
}
@VisibleForTesting
@@ -129,14 +113,15 @@
* @param parsedPackage the {@link ParsedPackage} to modify.
*/
@VisibleForTesting
- public static void modifySharedLibraries(ParsedPackage parsedPackage) {
- INSTANCE.updatePackage(parsedPackage);
+ public static void modifySharedLibraries(ParsedPackage parsedPackage,
+ boolean isUpdatedSystemApp) {
+ INSTANCE.updatePackage(parsedPackage, isUpdatedSystemApp);
}
@Override
- public void updatePackage(ParsedPackage parsedPackage) {
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
- packageUpdater.updatePackage(parsedPackage);
+ packageUpdater.updatePackage(parsedPackage, isUpdatedSystemApp);
}
}
@@ -161,7 +146,7 @@
public static class AndroidTestRunnerSplitUpdater extends PackageSharedLibraryUpdater {
@Override
- public void updatePackage(ParsedPackage parsedPackage) {
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
// 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.
prefixImplicitDependency(parsedPackage, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
@@ -177,7 +162,7 @@
extends PackageSharedLibraryUpdater {
@Override
- public void updatePackage(ParsedPackage parsedPackage) {
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
removeLibrary(parsedPackage, ORG_APACHE_HTTP_LEGACY);
}
@@ -192,7 +177,7 @@
extends PackageSharedLibraryUpdater {
@Override
- public void updatePackage(ParsedPackage parsedPackage) {
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
removeLibrary(parsedPackage, ANDROID_TEST_BASE);
}
}
diff --git a/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
similarity index 94%
rename from core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java
rename to services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
index 8b27d14..123b808 100644
--- a/core/java/android/content/pm/parsing/library/PackageSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.parsing.ParsedPackage;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.util.ArrayList;
import java.util.List;
@@ -38,7 +38,7 @@
*
* @param parsedPackage the package to update.
*/
- public abstract void updatePackage(ParsedPackage parsedPackage);
+ public abstract void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp);
static void removeLibrary(ParsedPackage parsedPackage, String libraryName) {
parsedPackage.removeUsesLibrary(libraryName)
diff --git a/core/java/android/content/pm/parsing/library/SharedLibraryNames.java b/services/core/java/com/android/server/pm/parsing/library/SharedLibraryNames.java
similarity index 91%
rename from core/java/android/content/pm/parsing/library/SharedLibraryNames.java
rename to services/core/java/com/android/server/pm/parsing/library/SharedLibraryNames.java
index 7b691c0..f62f014 100644
--- a/core/java/android/content/pm/parsing/library/SharedLibraryNames.java
+++ b/services/core/java/com/android/server/pm/parsing/library/SharedLibraryNames.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
/**
* A set of shared library names
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.aidl b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.aidl
similarity index 100%
rename from core/java/android/content/pm/parsing/AndroidPackage.aidl
rename to services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.aidl
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
new file mode 100644
index 0000000..7929579
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.component.ParsedFeature;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.internal.R;
+
+import java.security.PublicKey;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * The last state of a package during parsing/install before it is available in
+ * {@link com.android.server.pm.PackageManagerService#mPackages}.
+ *
+ * It is the responsibility of the caller to understand what data is available at what step of the
+ * parsing or install process.
+ *
+ * TODO(b/135203078): Nullability annotations
+ * TODO(b/135203078): Remove get/setAppInfo differences
+ *
+ * @hide
+ */
+public interface AndroidPackage extends PkgAppInfo, PkgPackageInfo, ParsingPackageRead, Parcelable {
+
+ /**
+ * The names of packages to adopt ownership of permissions from, parsed under
+ * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+ * @see R.styleable#AndroidManifestOriginalPackage_name
+ */
+ @NonNull
+ List<String> getAdoptPermissions();
+
+ /** Path of base APK */
+ @NonNull
+ String getBaseCodePath();
+
+ /** Revision code of base APK */
+ int getBaseRevisionCode();
+
+ /**
+ * Path where this package was found on disk. For monolithic packages
+ * this is path to single base APK file; for cluster packages this is
+ * path to the cluster directory.
+ */
+ @NonNull
+ String getCodePath();
+
+ /**
+ * Permissions requested but not in the manifest. These may have been split or migrated from
+ * previous versions/definitions.
+ */
+ @NonNull
+ List<String> getImplicitPermissions();
+
+ /**
+ * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
+ * {@link PackageParser#TAG_KEY_SETS}.
+ * @see R.styleable#AndroidManifestKeySet
+ * @see R.styleable#AndroidManifestPublicKey
+ */
+ @NonNull
+ Map<String, ArraySet<PublicKey>> getKeySetMapping();
+
+ /**
+ * Library names this package is declared as, for use by other packages with "uses-library".
+ * @see R.styleable#AndroidManifestLibrary
+ */
+ @NonNull
+ List<String> getLibraryNames();
+
+ /**
+ * The package name as declared in the manifest, since the package can be renamed. For example,
+ * static shared libs use synthetic package names.
+ */
+ @NonNull
+ String getManifestPackageName();
+
+ /**
+ * We store the application meta-data independently to avoid multiple unwanted references
+ * TODO(b/135203078): What does this comment mean?
+ * TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
+ */
+ @Nullable
+ Bundle getMetaData();
+
+ /**
+ * For system use to migrate from an old package name to a new one, moving over data
+ * if available.
+ * @see R.styleable#AndroidManifestOriginalPackage}
+ */
+ @NonNull
+ List<String> getOriginalPackages();
+
+ /**
+ * Map of overlayable name to actor name.
+ */
+ @NonNull
+ Map<String, String> getOverlayables();
+
+ /**
+ * The name of the package as used to identify it in the system. This may be adjusted by the
+ * system from the value declared in the manifest, and may not correspond to a Java code
+ * package.
+ * @see ApplicationInfo#packageName
+ * @see PackageInfo#packageName
+ */
+ @NonNull
+ String getPackageName();
+
+ /**
+ * @see PermissionGroupInfo
+ */
+ @NonNull
+ List<ParsedPermissionGroup> getPermissionGroups();
+
+ @NonNull
+ List<ParsedFeature> getFeatures();
+
+ /**
+ * Used to determine the default preferred handler of an {@link Intent}.
+ *
+ * Map of component className to intent info inside that component.
+ * TODO(b/135203078): Is this actually used/working?
+ */
+ @NonNull
+ List<Pair<String, ParsedIntentInfo>> getPreferredActivityFilters();
+
+ /**
+ * System protected broadcasts.
+ * @see R.styleable#AndroidManifestProtectedBroadcast
+ */
+ @NonNull
+ List<String> getProtectedBroadcasts();
+
+ /**
+ * Intents that this package may query or require and thus requires visibility into.
+ * @see R.styleable#AndroidManifestQueriesIntent
+ */
+ @NonNull
+ List<Intent> getQueriesIntents();
+
+ /**
+ * Other packages that this package may query or require and thus requires visibility into.
+ * @see R.styleable#AndroidManifestQueriesPackage
+ */
+ @NonNull
+ List<String> getQueriesPackages();
+
+ /**
+ * If a system app declares {@link #getOriginalPackages()}, and the app was previously installed
+ * under one of those original package names, the {@link #getPackageName()} system identifier
+ * will be changed to that previously installed name. This will then be non-null, set to the
+ * manifest package name, for tracking the package under its true name.
+ *
+ * TODO(b/135203078): Remove this in favor of checking originalPackages.isEmpty and
+ * getManifestPackageName
+ */
+ @Nullable
+ String getRealPackage();
+
+ /**
+ * SHA-512 hash of the only APK that can be used to update a system package.
+ * @see R.styleable#AndroidManifestRestrictUpdate
+ */
+ @Nullable
+ byte[] getRestrictUpdateHash();
+
+ /**
+ * The signature data of all APKs in this package, which must be exactly the same across the
+ * base and splits.
+ */
+ PackageParser.SigningDetails getSigningDetails();
+
+ /**
+ * TODO(b/135203078): Move split stuff to an inner data class
+ * @see ApplicationInfo#splitNames
+ * @see PackageInfo#splitNames
+ */
+ @Nullable
+ String[] getSplitNames();
+
+ /** Flags of any split APKs; ordered by parsed splitName */
+ @Nullable
+ int[] getSplitFlags();
+
+ /** @see R.styleable#AndroidManifestStaticLibrary_name */
+ @Nullable
+ String getStaticSharedLibName();
+
+ /** @see R.styleable#AndroidManifestStaticLibrary_version */
+ long getStaticSharedLibVersion();
+
+ /**
+ * {@link android.os.storage.StorageManager#convert(String)} version of
+ * {@link #getVolumeUuid()}.
+ * TODO(b/135203078): All usages call toString() on this. Can the string be returned directly,
+ * or does the parsing logic in StorageManager have to run?
+ */
+ UUID getStorageUuid();
+
+ /**
+ * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
+ * {@link PackageParser#TAG_KEY_SETS}.
+ * @see R.styleable#AndroidManifestUpgradeKeySet
+ */
+ @NonNull
+ Set<String> getUpgradeKeySets();
+
+ /** @see R.styleable#AndroidManifestUsesLibrary */
+ @NonNull
+ List<String> getUsesLibraries();
+
+ /**
+ * Like {@link #getUsesLibraries()}, but marked optional by setting
+ * {@link R.styleable#AndroidManifestUsesLibrary_required} to false . Application is expected
+ * to handle absence manually.
+ * @see R.styleable#AndroidManifestUsesLibrary
+ */
+ @NonNull
+ List<String> getUsesOptionalLibraries();
+
+ /**
+ * TODO(b/135203078): Move static library stuff to an inner data class
+ * @see R.styleable#AndroidManifestUsesStaticLibrary
+ */
+ @NonNull
+ List<String> getUsesStaticLibraries();
+
+ /** @see R.styleable#AndroidManifestUsesStaticLibrary_certDigest */
+ @Nullable
+ String[][] getUsesStaticLibrariesCertDigests();
+
+ /** @see R.styleable#AndroidManifestUsesStaticLibrary_version */
+ @Nullable
+ long[] getUsesStaticLibrariesVersions();
+
+ /** @see R.styleable#AndroidManifestApplication_forceQueryable */
+ boolean isForceQueryable();
+
+ boolean isCrossProfile();
+
+ /**
+ * The install time abi override to choose 32bit abi's when multiple abi's
+ * are present. This is only meaningfull for multiarch applications.
+ */
+ boolean isUse32BitAbi();
+
+ /**
+ * Set if the any of components are visible to instant applications.
+ * @see R.styleable#AndroidManifestActivity_visibleToInstantApps
+ * @see R.styleable#AndroidManifestProvider_visibleToInstantApps
+ * @see R.styleable#AndroidManifestService_visibleToInstantApps
+ */
+ boolean isVisibleToInstantApps();
+
+ /**
+ * Generates an {@link ApplicationInfo} object with only the data available in this object.
+ *
+ * TODO(b/135203078): Actually add this
+ * This does not contain any system or user state data, and should be avoided. Prefer
+ * com.android.server.pm.parsing.PackageInfoUtils#generateApplicationInfo(
+ * AndroidPackage, int, PackageUserState, int, com.android.server.pm.PackageSetting)
+ *
+ * @deprecated Access AndroidPackage fields directly.
+ */
+ @Deprecated
+ @NonNull
+ ApplicationInfo toAppInfoWithoutState();
+
+ /**
+ * TODO(b/135203078): Remove usages?
+ * @return a mock of what the previous package.applicationInfo would've returned for logging
+ * @deprecated don't use this in any new code, just print package name directly
+ */
+ @Deprecated
+ @NonNull
+ String toAppInfoToString();
+}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
new file mode 100644
index 0000000..780b234
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.VersionedPackage;
+import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.text.TextUtils;
+
+import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
+import com.android.server.pm.PackageSetting;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/** @hide */
+public class AndroidPackageUtils {
+
+ private AndroidPackageUtils() {
+ }
+
+ public static List<String> getAllCodePathsExcludingResourceOnly(
+ AndroidPackage aPkg) {
+ PackageImpl pkg = (PackageImpl) aPkg;
+ ArrayList<String> paths = new ArrayList<>();
+ if (pkg.isHasCode()) {
+ paths.add(pkg.getBaseCodePath());
+ }
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ if ((pkg.getSplitFlags()[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ paths.add(splitCodePaths[i]);
+ }
+ }
+ }
+ return paths;
+ }
+
+ /**
+ * @return a list of the base and split code paths.
+ */
+ public static List<String> getAllCodePaths(AndroidPackage aPkg) {
+ PackageImpl pkg = (PackageImpl) aPkg;
+ ArrayList<String> paths = new ArrayList<>();
+ paths.add(pkg.getBaseCodePath());
+
+ String[] splitCodePaths = pkg.getSplitCodePaths();
+ if (!ArrayUtils.isEmpty(splitCodePaths)) {
+ Collections.addAll(paths, splitCodePaths);
+ }
+ return paths;
+ }
+
+ public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) {
+ return new SharedLibraryInfo(null, pkg.getPackageName(),
+ AndroidPackageUtils.getAllCodePaths(pkg),
+ pkg.getStaticSharedLibName(),
+ pkg.getStaticSharedLibVersion(),
+ SharedLibraryInfo.TYPE_STATIC,
+ new VersionedPackage(pkg.getManifestPackageName(),
+ pkg.getLongVersionCode()),
+ null, null);
+ }
+
+ public static SharedLibraryInfo createSharedLibraryForDynamic(AndroidPackage pkg, String name) {
+ return new SharedLibraryInfo(null, pkg.getPackageName(),
+ AndroidPackageUtils.getAllCodePaths(pkg), name,
+ SharedLibraryInfo.VERSION_UNDEFINED,
+ SharedLibraryInfo.TYPE_DYNAMIC, new VersionedPackage(pkg.getPackageName(),
+ pkg.getLongVersionCode()),
+ null, null);
+ }
+
+ /**
+ * Return the dex metadata files for the given package as a map
+ * [code path -> dex metadata path].
+ *
+ * NOTE: involves I/O checks.
+ */
+ public static Map<String, String> getPackageDexMetadata(AndroidPackage pkg) {
+ return DexMetadataHelper.buildPackageApkToDexMetadataMap
+ (AndroidPackageUtils.getAllCodePaths(pkg));
+ }
+
+ /**
+ * Validate the dex metadata files installed for the given package.
+ *
+ * @throws PackageParserException in case of errors.
+ */
+ public static void validatePackageDexMetadata(AndroidPackage pkg)
+ throws PackageParserException {
+ Collection<String> apkToDexMetadataList = getPackageDexMetadata(pkg).values();
+ for (String dexMetadata : apkToDexMetadataList) {
+ DexMetadataHelper.validateDexMetadataFile(dexMetadata);
+ }
+ }
+
+ public static NativeLibraryHelper.Handle createNativeLibraryHandle(AndroidPackage pkg)
+ throws IOException {
+ return NativeLibraryHelper.Handle.create(
+ AndroidPackageUtils.getAllCodePaths(pkg),
+ pkg.isMultiArch(),
+ pkg.isExtractNativeLibs(),
+ pkg.isDebuggable()
+ );
+ }
+
+ public static boolean canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp) {
+ // The following app types CANNOT have oat directory
+ // - non-updated system apps
+ return !pkg.isSystem() || isUpdatedSystemApp;
+ }
+
+ public static boolean hasComponentClassName(AndroidPackage pkg, String className) {
+ List<ParsedActivity> activities = pkg.getActivities();
+ int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ if (Objects.equals(className, activities.get(index).getName())) {
+ return true;
+ }
+ }
+
+ List<ParsedActivity> receivers = pkg.getReceivers();
+ int receiversSize = receivers.size();
+ for (int index = 0; index < receiversSize; index++) {
+ if (Objects.equals(className, receivers.get(index).getName())) {
+ return true;
+ }
+ }
+
+ List<ParsedProvider> providers = pkg.getProviders();
+ int providersSize = providers.size();
+ for (int index = 0; index < providersSize; index++) {
+ if (Objects.equals(className, providers.get(index).getName())) {
+ return true;
+ }
+ }
+
+ List<ParsedService> services = pkg.getServices();
+ int servicesSize = services.size();
+ for (int index = 0; index < servicesSize; index++) {
+ if (Objects.equals(className, services.get(index).getName())) {
+ return true;
+ }
+ }
+
+ List<ParsedInstrumentation> instrumentations = pkg.getInstrumentations();
+ int instrumentationsSize = instrumentations.size();
+ for (int index = 0; index < instrumentationsSize; index++) {
+ if (Objects.equals(className, instrumentations.get(index).getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean isEncryptionAware(AndroidPackage pkg) {
+ return pkg.isDirectBootAware() || pkg.isPartiallyDirectBootAware();
+ }
+
+ public static boolean isLibrary(AndroidPackage pkg) {
+ // TODO(b/135203078): Can parsing just enforce these always match?
+ return pkg.getStaticSharedLibName() != null || !pkg.getLibraryNames().isEmpty();
+ }
+
+ public static int getHiddenApiEnforcementPolicy(AndroidPackage pkg,
+ @NonNull PackageSetting pkgSetting) {
+ boolean isAllowedToUseHiddenApis;
+ if (pkg.isSignedWithPlatformKey()) {
+ isAllowedToUseHiddenApis = true;
+ } else if (pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
+ isAllowedToUseHiddenApis = pkg.isUsesNonSdkApi()
+ || SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(
+ pkg.getPackageName());
+ } else {
+ isAllowedToUseHiddenApis = false;
+ }
+
+ if (isAllowedToUseHiddenApis) {
+ return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+ }
+
+ // TODO(b/135203078): Handle maybeUpdateHiddenApiEnforcementPolicy. Right now it's done
+ // entirely through ApplicationInfo and shouldn't touch this specific class, but that
+ // may not always hold true.
+// if (mHiddenApiPolicy != ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT) {
+// return mHiddenApiPolicy;
+// }
+ return ApplicationInfo.HIDDEN_API_ENFORCEMENT_ENABLED;
+ }
+
+ public static int getIcon(ParsingPackageRead pkg) {
+ return (PackageParser.sUseRoundIcon && pkg.getRoundIconRes() != 0)
+ ? pkg.getRoundIconRes() : pkg.getIconRes();
+ }
+
+ public static long getLongVersionCode(AndroidPackage pkg) {
+ return PackageInfo.composeLongVersionCode(pkg.getVersionCodeMajor(), pkg.getVersionCode());
+ }
+
+ public static boolean isMatchForSystemOnly(AndroidPackage pkg, int flags) {
+ if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+ return pkg.isSystem();
+ }
+ return true;
+ }
+
+ public static String getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.primaryCpuAbiString)) {
+ return pkg.getPrimaryCpuAbi();
+ }
+
+ return pkgSetting.primaryCpuAbiString;
+ }
+
+ public static String getSecondaryCpuAbi(AndroidPackage pkg,
+ @Nullable PackageSetting pkgSetting) {
+ if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.secondaryCpuAbiString)) {
+ return pkg.getSecondaryCpuAbi();
+ }
+
+ return pkgSetting.secondaryCpuAbiString;
+ }
+
+ /**
+ * Returns the primary ABI as parsed from the package. Used only during parsing and derivation.
+ * Otherwise prefer {@link #getPrimaryCpuAbi(AndroidPackage, PackageSetting)}.
+ *
+ * TODO(b/135203078): Actually hide the method
+ * Placed in the utility to hide the method on the interface.
+ */
+ public static String getRawPrimaryCpuAbi(AndroidPackage pkg) {
+ return pkg.getPrimaryCpuAbi();
+ }
+
+ /**
+ * Returns the secondary ABI as parsed from the package. Used only during parsing and
+ * derivation. Otherwise prefer {@link #getSecondaryCpuAbi(AndroidPackage, PackageSetting)}.
+ *
+ * TODO(b/135203078): Actually hide the method
+ * Placed in the utility to hide the method on the interface.
+ */
+ public static String getRawSecondaryCpuAbi(AndroidPackage pkg) {
+ return pkg.getSecondaryCpuAbi();
+ }
+
+ public static String getSeInfo(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
+ if (pkgSetting != null) {
+ String overrideSeInfo = pkgSetting.getPkgState().getOverrideSeInfo();
+ if (!TextUtils.isEmpty(overrideSeInfo)) {
+ return overrideSeInfo;
+ }
+ }
+ return pkg.getSeInfo();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
new file mode 100644
index 0000000..2b508ea
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java
@@ -0,0 +1,752 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageImpl;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedMainComponent;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+import android.content.res.TypedArray;
+import android.os.Environment;
+import android.os.Parcel;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Parcelling;
+import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
+import com.android.server.pm.parsing.PackageInfoUtils;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Extensions to {@link ParsingPackageImpl} including fields/state contained in the system server
+ * and not exposed to the core SDK.
+ *
+ * Many of the fields contained here will eventually be moved inside
+ * {@link com.android.server.pm.PackageSetting} or {@link android.content.pm.PackageUserState}.
+ *
+ * @hide
+ */
+public final class PackageImpl extends ParsingPackageImpl implements ParsedPackage, AndroidPackage {
+
+ public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
+ @NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) {
+ return new PackageImpl(packageName, baseCodePath, codePath, manifestArray, isCoreApp);
+ }
+
+ /**
+ * Mock an unavailable {@link AndroidPackage} to use when
+ * removing
+ * a package from the system.
+ * This can occur if the package was installed on a storage device that has since been removed.
+ * Since the infrastructure uses {@link AndroidPackage}, but
+ * for
+ * this case only cares about
+ * volumeUuid, just fake it rather than having separate method paths.
+ */
+ public static AndroidPackage buildFakeForDeletion(String packageName, String volumeUuid) {
+ return ((ParsedPackage) PackageImpl.forTesting(packageName)
+ .setVolumeUuid(volumeUuid)
+ .hideAsParsed())
+ .hideAsFinal();
+ }
+
+ @VisibleForTesting
+ public static ParsingPackage forTesting(String packageName) {
+ return forTesting(packageName, "");
+ }
+
+ @VisibleForTesting
+ public static ParsingPackage forTesting(String packageName, String baseCodePath) {
+ return new PackageImpl(packageName, baseCodePath, baseCodePath, null, false);
+ }
+
+ @NonNull
+ @DataClass.ParcelWith(ForInternedString.class)
+ private final String manifestPackageName;
+
+ private boolean stub;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String nativeLibraryDir;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String nativeLibraryRootDir;
+
+ private boolean nativeLibraryRootRequiresIsa;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String primaryCpuAbi;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String secondaryCpuAbi;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String secondaryNativeLibraryDir;
+
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String seInfo;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
+ protected String seInfoUser;
+
+ private boolean coreApp;
+
+ private boolean system;
+ private boolean factoryTest;
+
+ private boolean systemExt;
+ private boolean privileged;
+ private boolean oem;
+ private boolean vendor;
+ private boolean product;
+ private boolean odm;
+
+ private boolean signedWithPlatformKey;
+
+ /**
+ * This is an appId, the uid if the userId is == USER_SYSTEM
+ */
+ private int uid = -1;
+
+ @VisibleForTesting
+ public PackageImpl(@NonNull String packageName, @NonNull String baseCodePath,
+ @NonNull String codePath, @Nullable TypedArray manifestArray, boolean isCoreApp) {
+ super(packageName, baseCodePath, codePath, manifestArray);
+ this.manifestPackageName = this.packageName;
+ this.coreApp = isCoreApp;
+ }
+
+ @Override
+ public ParsedPackage hideAsParsed() {
+ return this;
+ }
+
+ @Override
+ public AndroidPackage hideAsFinal() {
+ // TODO(b/135203078): Lock as immutable
+ return this;
+ }
+
+ @Override
+ public long getLongVersionCode() {
+ return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+ }
+
+ @Override
+ public PackageImpl removePermission(int index) {
+ this.permissions.remove(index);
+ return this;
+ }
+
+ @Override
+ public PackageImpl addUsesOptionalLibrary(int index, String libraryName) {
+ this.usesOptionalLibraries = CollectionUtils.add(usesOptionalLibraries, index,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public PackageImpl addUsesLibrary(int index, String libraryName) {
+ this.usesLibraries = CollectionUtils.add(usesLibraries, index,
+ TextUtils.safeIntern(libraryName));
+ return this;
+ }
+
+ @Override
+ public PackageImpl removeUsesLibrary(String libraryName) {
+ this.usesLibraries = CollectionUtils.remove(this.usesLibraries, libraryName);
+ return this;
+ }
+
+ @Override
+ public PackageImpl removeUsesOptionalLibrary(String libraryName) {
+ super.removeUsesOptionalLibrary(libraryName);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSigningDetails(@Nullable PackageParser.SigningDetails value) {
+ super.setSigningDetails(value);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setRestrictUpdateHash(@Nullable byte... value) {
+ super.setRestrictUpdateHash(value);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setRealPackage(@Nullable String realPackage) {
+ super.setRealPackage(realPackage);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setPersistent(boolean value) {
+ super.setPersistent(value);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setDefaultToDeviceProtectedStorage(boolean value) {
+ super.setDefaultToDeviceProtectedStorage(value);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setDirectBootAware(boolean value) {
+ super.setDirectBootAware(value);
+ return this;
+ }
+
+ @Override
+ public PackageImpl clearProtectedBroadcasts() {
+ protectedBroadcasts.clear();
+ return this;
+ }
+
+ @Override
+ public PackageImpl clearOriginalPackages() {
+ originalPackages.clear();
+ return this;
+ }
+
+ @Override
+ public PackageImpl clearAdoptPermissions() {
+ adoptPermissions.clear();
+ return this;
+ }
+
+ @Override
+ public PackageImpl setCodePath(@NonNull String value) {
+ this.codePath = value;
+ return this;
+ }
+
+ // TODO(b/135203078): Move PackageManagerService#renameStaticSharedLibraryPackage
+ // into initial package parsing
+ @Override
+ public PackageImpl setPackageName(@NonNull String packageName) {
+ this.packageName = TextUtils.safeIntern(packageName);
+
+ int permissionsSize = permissions.size();
+ for (int index = 0; index < permissionsSize; index++) {
+ permissions.get(index).setPackageName(this.packageName);
+ }
+
+ int permissionGroupsSize = permissionGroups.size();
+ for (int index = 0; index < permissionGroupsSize; index++) {
+ permissionGroups.get(index).setPackageName(this.packageName);
+ }
+
+ int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ activities.get(index).setPackageName(this.packageName);
+ }
+
+ int receiversSize = receivers.size();
+ for (int index = 0; index < receiversSize; index++) {
+ receivers.get(index).setPackageName(this.packageName);
+ }
+
+ int providersSize = providers.size();
+ for (int index = 0; index < providersSize; index++) {
+ providers.get(index).setPackageName(this.packageName);
+ }
+
+ int servicesSize = services.size();
+ for (int index = 0; index < servicesSize; index++) {
+ services.get(index).setPackageName(this.packageName);
+ }
+
+ int instrumentationsSize = instrumentations.size();
+ for (int index = 0; index < instrumentationsSize; index++) {
+ instrumentations.get(index).setPackageName(this.packageName);
+ }
+
+ return this;
+ }
+
+ @Override
+ public PackageImpl setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware) {
+ int activitiesSize = activities.size();
+ for (int index = 0; index < activitiesSize; index++) {
+ activities.get(index).setDirectBootAware(allComponentsDirectBootAware);
+ }
+
+ int receiversSize = receivers.size();
+ for (int index = 0; index < receiversSize; index++) {
+ receivers.get(index).setDirectBootAware(allComponentsDirectBootAware);
+ }
+
+ int providersSize = providers.size();
+ for (int index = 0; index < providersSize; index++) {
+ providers.get(index).setDirectBootAware(allComponentsDirectBootAware);
+ }
+
+ int servicesSize = services.size();
+ for (int index = 0; index < servicesSize; index++) {
+ services.get(index).setDirectBootAware(allComponentsDirectBootAware);
+ }
+
+ return this;
+ }
+
+ @Override
+ public PackageImpl setBaseCodePath(@NonNull String baseCodePath) {
+ this.baseCodePath = TextUtils.safeIntern(baseCodePath);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setNativeLibraryDir(@Nullable String nativeLibraryDir) {
+ this.nativeLibraryDir = TextUtils.safeIntern(nativeLibraryDir);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setNativeLibraryRootDir(@Nullable String nativeLibraryRootDir) {
+ this.nativeLibraryRootDir = TextUtils.safeIntern(nativeLibraryRootDir);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setPrimaryCpuAbi(@Nullable String primaryCpuAbi) {
+ this.primaryCpuAbi = TextUtils.safeIntern(primaryCpuAbi);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSecondaryCpuAbi(@Nullable String secondaryCpuAbi) {
+ this.secondaryCpuAbi = TextUtils.safeIntern(secondaryCpuAbi);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSecondaryNativeLibraryDir(@Nullable String secondaryNativeLibraryDir) {
+ this.secondaryNativeLibraryDir = TextUtils.safeIntern(secondaryNativeLibraryDir);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSeInfo(@Nullable String seInfo) {
+ this.seInfo = TextUtils.safeIntern(seInfo);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSeInfoUser(@Nullable String seInfoUser) {
+ this.seInfoUser = TextUtils.safeIntern(seInfoUser);
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSplitCodePaths(@Nullable String[] splitCodePaths) {
+ this.splitCodePaths = splitCodePaths;
+ if (splitCodePaths != null) {
+ int size = splitCodePaths.length;
+ for (int index = 0; index < size; index++) {
+ this.splitCodePaths[index] = TextUtils.safeIntern(this.splitCodePaths[index]);
+ }
+ }
+ return this;
+ }
+
+ @Override
+ public PackageImpl capPermissionPriorities() {
+ int size = permissionGroups.size();
+ for (int index = size - 1; index >= 0; --index) {
+ // TODO(b/135203078): Builder/immutability
+ permissionGroups.get(index).setPriority(0);
+ }
+ return this;
+ }
+
+ @Override
+ public PackageImpl markNotActivitiesAsNotExportedIfSingleUser() {
+ // ignore export request for single user receivers
+ int receiversSize = receivers.size();
+ for (int index = 0; index < receiversSize; index++) {
+ ParsedActivity receiver = receivers.get(index);
+ if ((receiver.getFlags() & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ receiver.setExported(false);
+ }
+ }
+
+ // ignore export request for single user services
+ int servicesSize = services.size();
+ for (int index = 0; index < servicesSize; index++) {
+ ParsedService service = services.get(index);
+ if ((service.getFlags() & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ service.setExported(false);
+ }
+ }
+
+ // ignore export request for single user providers
+ int providersSize = providers.size();
+ for (int index = 0; index < providersSize; index++) {
+ ParsedProvider provider = providers.get(index);
+ if ((provider.getFlags() & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+ provider.setExported(false);
+ }
+ }
+
+ return this;
+ }
+
+ @Override
+ public UUID getStorageUuid() {
+ return StorageManager.convert(volumeUuid);
+ }
+
+ @Deprecated
+ @Override
+ public String toAppInfoToString() {
+ return "ApplicationInfo{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + getPackageName() + "}";
+ }
+
+ @Override
+ public ParsedPackage setCoreApp(boolean coreApp) {
+ this.coreApp = coreApp;
+ return this;
+ }
+
+ @Override
+ public ParsedPackage setVersionCode(int versionCode) {
+ this.versionCode = versionCode;
+ return this;
+ }
+
+ @Override
+ public ParsedPackage setVersionCodeMajor(int versionCodeMajor) {
+ this.versionCodeMajor = versionCodeMajor;
+ return this;
+ }
+
+ @Override
+ public ApplicationInfo toAppInfoWithoutState() {
+ ApplicationInfo appInfo = super.toAppInfoWithoutState();
+ appInfo.flags = PackageInfoUtils.appInfoFlags(this, null);
+ appInfo.privateFlags = PackageInfoUtils.appInfoPrivateFlags(this, null);
+ appInfo.nativeLibraryDir = nativeLibraryDir;
+ appInfo.nativeLibraryRootDir = nativeLibraryRootDir;
+ appInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa;
+ appInfo.primaryCpuAbi = primaryCpuAbi;
+ appInfo.secondaryCpuAbi = secondaryCpuAbi;
+ appInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir;
+ appInfo.seInfo = seInfo;
+ appInfo.seInfoUser = seInfoUser;
+ appInfo.uid = uid;
+ return appInfo;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ sForString.parcel(this.manifestPackageName, dest, flags);
+ dest.writeBoolean(this.stub);
+ sForString.parcel(this.nativeLibraryDir, dest, flags);
+ sForString.parcel(this.nativeLibraryRootDir, dest, flags);
+ dest.writeBoolean(this.nativeLibraryRootRequiresIsa);
+ sForString.parcel(this.primaryCpuAbi, dest, flags);
+ sForString.parcel(this.secondaryCpuAbi, dest, flags);
+ sForString.parcel(this.secondaryNativeLibraryDir, dest, flags);
+ sForString.parcel(this.seInfo, dest, flags);
+ sForString.parcel(this.seInfoUser, dest, flags);
+ dest.writeInt(this.uid);
+ dest.writeBoolean(this.coreApp);
+ dest.writeBoolean(this.system);
+ dest.writeBoolean(this.factoryTest);
+ dest.writeBoolean(this.systemExt);
+ dest.writeBoolean(this.privileged);
+ dest.writeBoolean(this.oem);
+ dest.writeBoolean(this.vendor);
+ dest.writeBoolean(this.product);
+ dest.writeBoolean(this.odm);
+ dest.writeBoolean(this.signedWithPlatformKey);
+ }
+
+ public PackageImpl(Parcel in) {
+ super(in);
+ this.manifestPackageName = sForString.unparcel(in);
+ this.stub = in.readBoolean();
+ this.nativeLibraryDir = sForString.unparcel(in);
+ this.nativeLibraryRootDir = sForString.unparcel(in);
+ this.nativeLibraryRootRequiresIsa = in.readBoolean();
+ this.primaryCpuAbi = sForString.unparcel(in);
+ this.secondaryCpuAbi = sForString.unparcel(in);
+ this.secondaryNativeLibraryDir = sForString.unparcel(in);
+ this.seInfo = sForString.unparcel(in);
+ this.seInfoUser = sForString.unparcel(in);
+ this.uid = in.readInt();
+ this.coreApp = in.readBoolean();
+ this.system = in.readBoolean();
+ this.factoryTest = in.readBoolean();
+ this.systemExt = in.readBoolean();
+ this.privileged = in.readBoolean();
+ this.oem = in.readBoolean();
+ this.vendor = in.readBoolean();
+ this.product = in.readBoolean();
+ this.odm = in.readBoolean();
+ this.signedWithPlatformKey = in.readBoolean();
+ }
+
+ public static final Creator<PackageImpl> CREATOR = new Creator<PackageImpl>() {
+ @Override
+ public PackageImpl createFromParcel(Parcel source) {
+ return new PackageImpl(source);
+ }
+
+ @Override
+ public PackageImpl[] newArray(int size) {
+ return new PackageImpl[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String getManifestPackageName() {
+ return manifestPackageName;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isStub() {
+ return stub;
+ }
+
+ @Nullable
+ @Override
+ public String getNativeLibraryDir() {
+ return nativeLibraryDir;
+ }
+
+ @Nullable
+ @Override
+ public String getNativeLibraryRootDir() {
+ return nativeLibraryRootDir;
+ }
+
+ @Override
+ public boolean isNativeLibraryRootRequiresIsa() {
+ return nativeLibraryRootRequiresIsa;
+ }
+
+ @Nullable
+ @Override
+ public String getPrimaryCpuAbi() {
+ return primaryCpuAbi;
+ }
+
+ @Nullable
+ @Override
+ public String getSecondaryCpuAbi() {
+ return secondaryCpuAbi;
+ }
+
+ @Nullable
+ @Override
+ public String getSecondaryNativeLibraryDir() {
+ return secondaryNativeLibraryDir;
+ }
+
+ @Nullable
+ @Override
+ public String getSeInfo() {
+ return seInfo;
+ }
+
+ @Nullable
+ @Override
+ public String getSeInfoUser() {
+ return seInfoUser;
+ }
+
+ @Override
+ public boolean isCoreApp() {
+ return coreApp;
+ }
+
+ @Override
+ public boolean isSystem() {
+ return system;
+ }
+
+ @Override
+ public boolean isFactoryTest() {
+ return factoryTest;
+ }
+
+ @Override
+ public boolean isSystemExt() {
+ return systemExt;
+ }
+
+ @Override
+ public boolean isPrivileged() {
+ return privileged;
+ }
+
+ @Override
+ public boolean isOem() {
+ return oem;
+ }
+
+ @Override
+ public boolean isVendor() {
+ return vendor;
+ }
+
+ @Override
+ public boolean isProduct() {
+ return product;
+ }
+
+ @Override
+ public boolean isOdm() {
+ return odm;
+ }
+
+ @Override
+ public boolean isSignedWithPlatformKey() {
+ return signedWithPlatformKey;
+ }
+
+ /**
+ * This is an appId, the uid if the userId is == USER_SYSTEM
+ */
+ @Override
+ public int getUid() {
+ return uid;
+ }
+
+ @DataClass.Generated.Member
+ public PackageImpl setStub(boolean value) {
+ stub = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setNativeLibraryRootRequiresIsa(boolean value) {
+ nativeLibraryRootRequiresIsa = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSystem(boolean value) {
+ system = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setFactoryTest(boolean value) {
+ factoryTest = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSystemExt(boolean value) {
+ systemExt = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setPrivileged(boolean value) {
+ privileged = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setOem(boolean value) {
+ oem = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setVendor(boolean value) {
+ vendor = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setProduct(boolean value) {
+ product = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setOdm(boolean value) {
+ odm = value;
+ return this;
+ }
+
+ @Override
+ public PackageImpl setSignedWithPlatformKey(boolean value) {
+ signedWithPlatformKey = value;
+ return this;
+ }
+
+ /**
+ * This is an appId, the uid if the userId is == USER_SYSTEM
+ */
+ @Override
+ public PackageImpl setUid(int value) {
+ uid = value;
+ return this;
+ }
+
+ @DataClass.Generated(
+ time = 1580517688900L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java",
+ inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String manifestPackageName\nprivate boolean stub\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String nativeLibraryRootDir\nprivate boolean nativeLibraryRootRequiresIsa\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String primaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryCpuAbi\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String secondaryNativeLibraryDir\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfo\nprotected @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String seInfoUser\nprivate boolean system\nprivate boolean factoryTest\nprivate boolean systemExt\nprivate boolean privileged\nprivate boolean oem\nprivate boolean vendor\nprivate boolean product\nprivate boolean odm\nprivate boolean signedWithPlatformKey\nprivate int uid\npublic static final com.android.server.pm.parsing.pkg.Creator<com.android.server.pm.parsing.pkg.PackageImpl> CREATOR\npublic static com.android.server.pm.parsing.pkg.PackageImpl forParsing(java.lang.String,java.lang.String,java.lang.String,android.content.res.TypedArray,boolean)\npublic static com.android.server.pm.parsing.pkg.AndroidPackage buildFakeForDeletion(java.lang.String,java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String)\npublic static @com.android.internal.annotations.VisibleForTesting android.content.pm.parsing.ParsingPackage forTesting(java.lang.String,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage hideAsParsed()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.AndroidPackage hideAsFinal()\npublic @java.lang.Override long getLongVersionCode()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removePermission(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesOptionalLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl addUsesLibrary(int,java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl removeUsesOptionalLibrary(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSigningDetails(android.content.pm.PackageParser.SigningDetails)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRestrictUpdateHash(byte)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setRealPackage(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPersistent(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDefaultToDeviceProtectedStorage(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearProtectedBroadcasts()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearOriginalPackages()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl clearAdoptPermissions()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPackageName(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setAllComponentsDirectBootAware(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setBaseCodePath(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setNativeLibraryRootDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setPrimaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryCpuAbi(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSecondaryNativeLibraryDir(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfo(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSeInfoUser(java.lang.String)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl setSplitCodePaths(java.lang.String[])\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl capPermissionPriorities()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.PackageImpl markNotActivitiesAsNotExportedIfSingleUser()\npublic @java.lang.Override java.util.UUID getStorageUuid()\npublic @java.lang.Deprecated @java.lang.Override java.lang.String toAppInfoToString()\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setCoreApp(boolean)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCode(int)\npublic @java.lang.Override com.android.server.pm.parsing.pkg.ParsedPackage setVersionCodeMajor(int)\npublic @java.lang.Override android.content.pm.ApplicationInfo toAppInfoWithoutState()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass PackageImpl extends android.content.pm.parsing.ParsingPackageImpl implements [com.android.server.pm.parsing.pkg.ParsedPackage, com.android.server.pm.parsing.pkg.AndroidPackage]\n@com.android.internal.util.DataClass(genConstructor=false, genParcelable=false, genAidl=false, genBuilder=false, genHiddenConstructor=false, genCopyConstructor=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/content/pm/parsing/ParsedPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
similarity index 71%
rename from core/java/android/content/pm/parsing/ParsedPackage.java
rename to services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
index 05cf586..2660f2b 100644
--- a/core/java/android/content/pm/parsing/ParsedPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/ParsedPackage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package android.content.pm.parsing;
+package com.android.server.pm.parsing.pkg;
+import android.annotation.Nullable;
import android.content.pm.PackageParser;
/**
@@ -42,26 +43,10 @@
ParsedPackage clearProtectedBroadcasts();
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #setCodePath(String)}
- */
- @Deprecated
- ParsedPackage setApplicationInfoCodePath(String applicationInfoCodePath);
-
- /**
- * TODO(b/135203078): Use non-AppInfo method
- * @deprecated use {@link #setCodePath(String)}
- */
- @Deprecated
- ParsedPackage setApplicationInfoResourcePath(String applicationInfoResourcePath);
-
ParsedPackage setBaseCodePath(String baseCodePath);
ParsedPackage setCodePath(String codePath);
- ParsedPackage setCpuAbiOverride(String cpuAbiOverride);
-
ParsedPackage setNativeLibraryDir(String nativeLibraryDir);
ParsedPackage setNativeLibraryRootDir(String nativeLibraryRootDir);
@@ -70,9 +55,7 @@
ParsedPackage setPrimaryCpuAbi(String primaryCpuAbi);
- ParsedPackage setProcessName(String processName);
-
- ParsedPackage setRealPackage(String realPackage);
+ ParsedPackage setRealPackage(@Nullable String realPackage);
ParsedPackage setSecondaryCpuAbi(String secondaryCpuAbi);
@@ -80,8 +63,6 @@
ParsedPackage setSplitCodePaths(String[] splitCodePaths);
- ParsedPackage initForUser(int userId);
-
ParsedPackage setNativeLibraryRootRequiresIsa(boolean nativeLibraryRootRequiresIsa);
ParsedPackage setAllComponentsDirectBootAware(boolean allComponentsDirectBootAware);
@@ -104,8 +85,6 @@
ParsedPackage setSystemExt(boolean systemExt);
- ParsedPackage setUpdatedSystemApp(boolean updatedSystemApp);
-
ParsedPackage setVendor(boolean vendor);
ParsedPackage removePermission(int index);
@@ -114,19 +93,9 @@
ParsedPackage removeUsesOptionalLibrary(String libraryName);
- ParsedPackage setApplicationInfoBaseResourcePath(String applicationInfoBaseResourcePath);
-
- ParsedPackage setApplicationInfoSplitResourcePaths(
- String[] applicationInfoSplitResourcePaths);
-
- ParsedPackage setApplicationVolumeUuid(String applicationVolumeUuid);
-
ParsedPackage setCoreApp(boolean coreApp);
- ParsedPackage setIsStub(boolean isStub);
-
- // TODO(b/135203078): Remove entirely
- ParsedPackage setPackageSettingCallback(PackageSettingCallback packageSettingCallback);
+ ParsedPackage setStub(boolean isStub);
ParsedPackage setRestrictUpdateHash(byte[] restrictUpdateHash);
@@ -148,8 +117,4 @@
ParsedPackage setDirectBootAware(boolean directBootAware);
ParsedPackage setPersistent(boolean persistent);
-
- interface PackageSettingCallback {
- default void setAndroidPackage(AndroidPackage pkg){}
- }
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java b/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
new file mode 100644
index 0000000..0cb425f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PkgAppInfo.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.util.SparseArray;
+
+import com.android.internal.R;
+
+/**
+ * Container for fields that are eventually exposed through {@link ApplicationInfo}.
+ *
+ * Done to separate the meaningless, re-directed JavaDoc for methods and to separate what's
+ * exposed vs not exposed to core.
+ *
+ * @hide
+ */
+interface PkgAppInfo {
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE */
+ boolean isCantSaveState();
+
+ /**
+ * @see ApplicationInfo#appComponentFactory
+ * @see R.styleable#AndroidManifestApplication_appComponentFactory
+ */
+ @Nullable
+ String getAppComponentFactory();
+
+ /**
+ * @see ApplicationInfo#backupAgentName
+ * @see R.styleable#AndroidManifestApplication_backupAgent
+ */
+ @Nullable
+ String getBackupAgentName();
+
+ /**
+ * @see ApplicationInfo#banner
+ * @see R.styleable#AndroidManifestApplication_banner
+ */
+ int getBanner();
+
+ /**
+ * @see ApplicationInfo#category
+ * @see R.styleable#AndroidManifestApplication_appCategory
+ */
+ int getCategory();
+
+ /**
+ * @see ApplicationInfo#classLoaderName
+ * @see R.styleable#AndroidManifestApplication_classLoader
+ */
+ @Nullable
+ String getClassLoaderName();
+
+ /**
+ * @see ApplicationInfo#className
+ * @see R.styleable#AndroidManifestApplication_name
+ */
+ @Nullable
+ String getClassName();
+
+ /**
+ * @see ApplicationInfo#compatibleWidthLimitDp
+ * @see R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
+ */
+ int getCompatibleWidthLimitDp();
+
+ /**
+ * @see ApplicationInfo#compileSdkVersion
+ * @see R.styleable#AndroidManifest_compileSdkVersion
+ */
+ int getCompileSdkVersion();
+
+ /**
+ * @see ApplicationInfo#compileSdkVersionCodename
+ * @see R.styleable#AndroidManifest_compileSdkVersionCodename
+ */
+ @Nullable
+ String getCompileSdkVersionCodeName();
+
+ /**
+ * @see ApplicationInfo#descriptionRes
+ * @see R.styleable#AndroidManifestApplication_description
+ */
+ int getDescriptionRes();
+
+ /**
+ * @see ApplicationInfo#fullBackupContent
+ * @see R.styleable#AndroidManifestApplication_fullBackupContent
+ */
+ int getFullBackupContent();
+
+ /**
+ * @see ApplicationInfo#iconRes
+ * @see R.styleable#AndroidManifestApplication_icon
+ */
+ int getIconRes();
+
+ /**
+ * @see ApplicationInfo#labelRes
+ * @see R.styleable#AndroidManifestApplication_label
+ */
+ int getLabelRes();
+
+ /**
+ * @see ApplicationInfo#largestWidthLimitDp
+ * @see R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
+ */
+ int getLargestWidthLimitDp();
+
+ /**
+ * @see ApplicationInfo#logo
+ * @see R.styleable#AndroidManifestApplication_logo
+ */
+ int getLogo();
+
+ /**
+ * @see ApplicationInfo#manageSpaceActivityName
+ * @see R.styleable#AndroidManifestApplication_manageSpaceActivity
+ */
+ @Nullable
+ String getManageSpaceActivityName();
+
+ /**
+ * @see ApplicationInfo#maxAspectRatio
+ * @see R.styleable#AndroidManifestApplication_maxAspectRatio
+ */
+ float getMaxAspectRatio();
+
+ /**
+ * @see ApplicationInfo#minAspectRatio
+ * @see R.styleable#AndroidManifestApplication_minAspectRatio
+ */
+ float getMinAspectRatio();
+
+ /**
+ * @see ApplicationInfo#minSdkVersion
+ * @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
+ */
+ int getMinSdkVersion();
+
+ /** @see ApplicationInfo#nativeLibraryDir */
+ @Nullable
+ String getNativeLibraryDir();
+
+ /** @see ApplicationInfo#nativeLibraryRootDir */
+ @Nullable
+ String getNativeLibraryRootDir();
+
+ /**
+ * @see ApplicationInfo#networkSecurityConfigRes
+ * @see R.styleable#AndroidManifestApplication_networkSecurityConfig
+ */
+ int getNetworkSecurityConfigRes();
+
+ /**
+ * If {@link R.styleable#AndroidManifestApplication_label} is a string literal, this is it.
+ * Otherwise, it's stored as {@link #getLabelRes()}.
+ * @see ApplicationInfo#nonLocalizedLabel
+ * @see R.styleable#AndroidManifestApplication_label
+ */
+ @Nullable
+ CharSequence getNonLocalizedLabel();
+
+ /**
+ * @see ApplicationInfo#permission
+ * @see R.styleable#AndroidManifestApplication_permission
+ */
+ @Nullable
+ String getPermission();
+
+ /**
+ * TODO(b/135203078): Hide this in the utility, should never be accessed directly
+ * @see ApplicationInfo#primaryCpuAbi
+ */
+ @Nullable
+ String getPrimaryCpuAbi();
+
+ /**
+ * @see ApplicationInfo#processName
+ * @see R.styleable#AndroidManifestApplication_process
+ */
+ @NonNull
+ String getProcessName();
+
+ /**
+ * @see ApplicationInfo#requiresSmallestWidthDp
+ * @see R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
+ */
+ int getRequiresSmallestWidthDp();
+
+ /**
+ * @see ApplicationInfo#roundIconRes
+ * @see R.styleable#AndroidManifestApplication_roundIcon
+ */
+ int getRoundIconRes();
+
+ /** @see ApplicationInfo#seInfo */
+ @Nullable
+ String getSeInfo();
+
+ /** @see ApplicationInfo#seInfoUser */
+ @Nullable
+ String getSeInfoUser();
+
+ /** @see ApplicationInfo#secondaryCpuAbi */
+ @Nullable
+ String getSecondaryCpuAbi();
+
+ /** @see ApplicationInfo#secondaryNativeLibraryDir */
+ @Nullable
+ String getSecondaryNativeLibraryDir();
+
+ /**
+ * @see ApplicationInfo#installLocation
+ * @see R.styleable#AndroidManifest_installLocation
+ */
+ int getInstallLocation();
+
+ /**
+ * @see ApplicationInfo#splitClassLoaderNames
+ * @see R.styleable#AndroidManifestApplication_classLoader
+ */
+ @Nullable
+ String[] getSplitClassLoaderNames();
+
+ /** @see ApplicationInfo#splitSourceDirs */
+ @Nullable
+ String[] getSplitCodePaths();
+
+ /** @see ApplicationInfo#splitDependencies */
+ @Nullable
+ SparseArray<int[]> getSplitDependencies();
+
+ /**
+ * @see ApplicationInfo#targetSandboxVersion
+ * @see R.styleable#AndroidManifest_targetSandboxVersion
+ */
+ @Deprecated
+ int getTargetSandboxVersion();
+
+ /**
+ * @see ApplicationInfo#targetSdkVersion
+ * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
+ */
+ int getTargetSdkVersion();
+
+ /**
+ * @see ApplicationInfo#taskAffinity
+ * @see R.styleable#AndroidManifestApplication_taskAffinity
+ */
+ @Nullable
+ String getTaskAffinity();
+
+ /**
+ * @see ApplicationInfo#theme
+ * @see R.styleable#AndroidManifestApplication_theme
+ */
+ int getTheme();
+
+ /**
+ * @see ApplicationInfo#uiOptions
+ * @see R.styleable#AndroidManifestApplication_uiOptions
+ */
+ int getUiOptions();
+
+ /** @see ApplicationInfo#uid */
+ int getUid();
+
+ /** @see ApplicationInfo#longVersionCode */
+ long getLongVersionCode();
+
+ /** @see ApplicationInfo#versionCode */
+ @Deprecated
+ int getVersionCode();
+
+ /** @see ApplicationInfo#volumeUuid */
+ @Nullable
+ String getVolumeUuid();
+
+ /** @see ApplicationInfo#zygotePreloadName */
+ @Nullable
+ String getZygotePreloadName();
+
+ /** @see ApplicationInfo#FLAG_HAS_CODE */
+ boolean isHasCode();
+
+ /** @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING */
+ boolean isAllowTaskReparenting();
+
+ /** @see ApplicationInfo#FLAG_MULTIARCH */
+ boolean isMultiArch();
+
+ /** @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS */
+ boolean isExtractNativeLibs();
+
+ /** @see ApplicationInfo#FLAG_DEBUGGABLE */
+ boolean isDebuggable();
+
+ /** @see ApplicationInfo#FLAG_VM_SAFE_MODE */
+ boolean isVmSafeMode();
+
+ /** @see ApplicationInfo#FLAG_PERSISTENT */
+ boolean isPersistent();
+
+ /** @see ApplicationInfo#FLAG_ALLOW_BACKUP */
+ boolean isAllowBackup();
+
+ /** @see ApplicationInfo#FLAG_TEST_ONLY */
+ boolean isTestOnly();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION */
+ boolean isResizeableActivityViaSdkVersion();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS */
+ boolean isHasDomainUrls();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE */
+ boolean isRequestLegacyExternalStorage();
+
+ /** @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED */
+ boolean isBaseHardwareAccelerated();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE */
+ boolean isDefaultToDeviceProtectedStorage();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE */
+ boolean isDirectBootAware();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE */
+ boolean isPartiallyDirectBootAware();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX */
+ boolean isUseEmbeddedDex();
+
+ /** @see ApplicationInfo#FLAG_EXTERNAL_STORAGE */
+ boolean isExternalStorage();
+
+ /** @see ApplicationInfo#nativeLibraryRootRequiresIsa */
+ boolean isNativeLibraryRootRequiresIsa();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ODM */
+ boolean isOdm();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_OEM */
+ boolean isOem();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_PRIVILEGED */
+ boolean isPrivileged();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_PRODUCT */
+ boolean isProduct();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL */
+ boolean isProfileableByShell();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY */
+ boolean isStaticSharedLibrary();
+
+ /** @see ApplicationInfo#FLAG_SYSTEM */
+ boolean isSystem();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_SYSTEM_EXT */
+ boolean isSystemExt();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_VENDOR */
+ boolean isVendor();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING */
+ boolean isIsolatedSplitLoading();
+
+ /**
+ * @see ApplicationInfo#enabled
+ * @see R.styleable#AndroidManifestApplication_enabled
+ */
+ boolean isEnabled();
+
+ /**
+ * @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
+ * @see ApplicationInfo#isResourceOverlay()
+ */
+ boolean isOverlay();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API */
+ boolean isUsesNonSdkApi();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY */
+ boolean isSignedWithPlatformKey();
+
+ /** @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE */
+ boolean isKillAfterRestore();
+
+ /** @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION */
+ boolean isRestoreAnyVersion();
+
+ /** @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY */
+ boolean isFullBackupOnly();
+
+ /** @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA */
+ boolean isAllowClearUserData();
+
+ /** @see ApplicationInfo#FLAG_LARGE_HEAP */
+ boolean isLargeHeap();
+
+ /** @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC */
+ boolean isUsesCleartextTraffic();
+
+ /** @see ApplicationInfo#FLAG_SUPPORTS_RTL */
+ boolean isSupportsRtl();
+
+ /** @see ApplicationInfo#FLAG_IS_GAME */
+ @Deprecated
+ boolean isGame();
+
+ /** @see ApplicationInfo#FLAG_FACTORY_TEST */
+ boolean isFactoryTest();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_smallScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_SMALL_SCREENS
+ */
+ boolean isSupportsSmallScreens();
+
+ /**
+ * If omitted from manifest, returns true.
+ * @see R.styleable#AndroidManifestSupportsScreens_normalScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_NORMAL_SCREENS
+ */
+ boolean isSupportsNormalScreens();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_largeScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_LARGE_SCREENS
+ */
+ boolean isSupportsLargeScreens();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD}.
+ * @see R.styleable#AndroidManifestSupportsScreens_xlargeScreens
+ * @see ApplicationInfo#FLAG_SUPPORTS_XLARGE_SCREENS
+ */
+ boolean isSupportsExtraLargeScreens();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_resizeable
+ * @see ApplicationInfo#FLAG_RESIZEABLE_FOR_SCREENS
+ */
+ boolean isResizeable();
+
+ /**
+ * If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >=
+ * {@link android.os.Build.VERSION_CODES#DONUT}.
+ * @see R.styleable#AndroidManifestSupportsScreens_anyDensity
+ * @see ApplicationInfo#FLAG_SUPPORTS_SCREEN_DENSITIES
+ */
+ boolean isAnyDensity();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND */
+ boolean isBackupInForeground();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE */
+ boolean isAllowClearUserDataOnFailedRestore();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE */
+ boolean isAllowAudioPlaybackCapture();
+
+ /** @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA */
+ boolean isHasFragileUserData();
+}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java b/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
new file mode 100644
index 0000000..89330a9
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/pkg/PkgPackageInfo.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.parsing.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureGroupInfo;
+import android.content.pm.FeatureInfo;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
+
+import com.android.internal.R;
+
+import java.util.List;
+
+/**
+ * Container for fields that are eventually exposed through {@link PackageInfo}.
+ *
+ * Done to separate the meaningless, re-directed JavaDoc for methods and to separate what's
+ * exposed vs not exposed to core.
+ *
+ * @hide
+ */
+interface PkgPackageInfo {
+
+ /**
+ * @see PackageInfo#overlayCategory
+ * @see R.styleable#AndroidManifestResourceOverlay_category
+ */
+ @Nullable
+ String getOverlayCategory();
+
+ /**
+ * @see PackageInfo#overlayPriority
+ * @see R.styleable#AndroidManifestResourceOverlay_priority
+ */
+ int getOverlayPriority();
+
+ /**
+ * @see PackageInfo#overlayTarget
+ * @see R.styleable#AndroidManifestResourceOverlay_targetPackage
+ */
+ @Nullable
+ String getOverlayTarget();
+
+ /**
+ * @see PackageInfo#targetOverlayableName
+ * @see R.styleable#AndroidManifestResourceOverlay_targetName
+ */
+ @Nullable
+ String getOverlayTargetName();
+
+ /**
+ * @see PackageInfo#sharedUserId
+ * @see R.styleable#AndroidManifest_sharedUserId
+ */
+ @Deprecated
+ @Nullable
+ String getSharedUserId();
+
+ /**
+ * @see PackageInfo#sharedUserLabel
+ * @see R.styleable#AndroidManifest_sharedUserLabel
+ */
+ @Deprecated
+ int getSharedUserLabel();
+
+ /**
+ * The required account type without which this application will not function.
+ *
+ * @see PackageInfo#requiredAccountType
+ * @see R.styleable#AndroidManifestApplication_requiredAccountType
+ */
+ @Nullable
+ String getRequiredAccountType();
+
+ /**
+ * The restricted account authenticator type that is used by this application
+ *
+ * @see PackageInfo#restrictedAccountType
+ * @see R.styleable#AndroidManifestApplication_restrictedAccountType
+ */
+ @Nullable
+ String getRestrictedAccountType();
+
+ /** @see PackageInfo#splitRevisionCodes */
+ int[] getSplitRevisionCodes();
+
+ /** @see PackageInfo#getLongVersionCode() */
+ long getLongVersionCode();
+
+ /** @see PackageInfo#versionCode */
+ @Deprecated
+ int getVersionCode();
+
+ /** @see PackageInfo#versionCodeMajor */
+ int getVersionCodeMajor();
+
+ /** @see PackageInfo#versionName */
+ @Nullable
+ String getVersionName();
+
+ /** @see PackageInfo#mOverlayIsStatic */
+ boolean isOverlayIsStatic();
+
+ /**
+ * @see PackageInfo#requiredForAllUsers
+ * @see R.styleable#AndroidManifestApplication_requiredForAllUsers
+ */
+ boolean isRequiredForAllUsers();
+
+ /**
+ * @see PackageInfo#reqFeatures
+ * @see R.styleable#AndroidManifestUsesFeature
+ */
+ @NonNull
+ List<FeatureInfo> getReqFeatures();
+
+ /**
+ * @see PackageInfo#configPreferences
+ * @see R.styleable#AndroidManifestUsesConfiguration
+ */
+ @NonNull
+ List<ConfigurationInfo> getConfigPreferences();
+
+ /**
+ * @see PackageInfo#featureGroups
+ * @see R.styleable#AndroidManifestUsesFeature
+ */
+ @NonNull
+ List<FeatureGroupInfo> getFeatureGroups();
+
+ /**
+ * Whether or not the package is a stub and must be replaced by the full version.
+ *
+ * @see PackageInfo#isStub
+ */
+ boolean isStub();
+
+ /**
+ * For marking packages required for a minimal boot state, through the "coreApp" manifest
+ * attribute.
+ * @see PackageInfo#coreApp
+ */
+ boolean isCoreApp();
+
+ /**
+ * All the permissions declared. This is an effective set, and may include permissions
+ * transformed from split/migrated permissions from previous versions, so may not be exactly
+ * what the package declares in its manifest.
+ * @see PackageInfo#requestedPermissions
+ * @see R.styleable#AndroidManifestUsesPermission
+ */
+ @NonNull
+ List<String> getRequestedPermissions();
+
+ /**
+ * @see ActivityInfo
+ * @see PackageInfo#activities
+ */
+ @NonNull
+ List<ParsedActivity> getActivities();
+
+ /**
+ * @see InstrumentationInfo
+ * @see PackageInfo#instrumentation
+ */
+ @NonNull
+ List<ParsedInstrumentation> getInstrumentations();
+
+ /**
+ * @see PermissionInfo
+ * @see PackageInfo#permissions
+ */
+ @NonNull
+ List<ParsedPermission> getPermissions();
+
+ /**
+ * @see ProviderInfo
+ * @see PackageInfo#providers
+ */
+ @NonNull
+ List<ParsedProvider> getProviders();
+
+ /**
+ * Since they share several attributes, receivers are parsed as {@link ParsedActivity}, even
+ * though they represent different functionality.
+ * TODO(b/135203078): Reconsider this and maybe make ParsedReceiver so it's not so confusing
+ * @see ActivityInfo
+ * @see PackageInfo#receivers
+ */
+ @NonNull
+ List<ParsedActivity> getReceivers();
+
+ /**
+ * @see ServiceInfo
+ * @see PackageInfo#services
+ */
+ @NonNull
+ List<ParsedService> getServices();
+}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index e323c98..f8e5082 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -32,9 +32,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.Signature;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.component.ParsedPermission;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -43,6 +41,8 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.PackageSettingBase;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -172,12 +172,12 @@
return 0;
}
- public boolean isPermission(@NonNull ParsedPermission perm) {
+ public boolean isPermission(ParsedPermission perm) {
if (this.perm == null) {
return false;
}
return Objects.equals(this.perm.getPackageName(), perm.getPackageName())
- && Objects.equals(this.perm.className, perm.className);
+ && Objects.equals(this.perm.getName(), perm.getName());
}
public boolean isDynamic() {
@@ -195,24 +195,24 @@
}
public boolean isRemoved() {
- return perm != null && (perm.flags & PermissionInfo.FLAG_REMOVED) != 0;
+ return perm != null && (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0;
}
public boolean isSoftRestricted() {
- return perm != null && (perm.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
+ return perm != null && (perm.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0;
}
public boolean isHardRestricted() {
- return perm != null && (perm.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
+ return perm != null && (perm.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
}
public boolean isHardOrSoftRestricted() {
- return perm != null && (perm.flags & (PermissionInfo.FLAG_HARD_RESTRICTED
+ return perm != null && (perm.getFlags() & (PermissionInfo.FLAG_HARD_RESTRICTED
| PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0;
}
public boolean isImmutablyRestricted() {
- return perm != null && (perm.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
+ return perm != null && (perm.getFlags() & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
}
public boolean isSignature() {
@@ -326,15 +326,8 @@
final BasePermission tree = findPermissionTree(permissionTrees, name);
if (tree != null && tree.perm != null) {
sourcePackageSetting = tree.sourcePackageSetting;
- perm = new ParsedPermission(tree.perm);
- perm.protectionLevel = pendingPermissionInfo.protectionLevel;
- perm.flags = pendingPermissionInfo.flags;
- perm.setGroup(pendingPermissionInfo.group);
- perm.backgroundPermission = pendingPermissionInfo.backgroundPermission;
- perm.descriptionRes = pendingPermissionInfo.descriptionRes;
- perm.requestRes = pendingPermissionInfo.requestRes;
- perm.setPackageName(tree.perm.getPackageName());
- perm.setName(name);
+ perm = new ParsedPermission(tree.perm, pendingPermissionInfo,
+ tree.perm.getPackageName(), name);
uid = tree.uid;
}
}
@@ -364,7 +357,7 @@
if (pkg.isSystem()) {
if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
// It's a built-in permission and no owner, take ownership now
- p.flags |= PermissionInfo.FLAG_INSTALLED;
+ p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
bp.sourcePackageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.getUid();
@@ -387,7 +380,7 @@
final BasePermission tree = findPermissionTree(permissionTrees, p.getName());
if (tree == null
|| tree.sourcePackageName.equals(p.getPackageName())) {
- p.flags |= PermissionInfo.FLAG_INSTALLED;
+ p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
bp.sourcePackageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.getUid();
@@ -421,8 +414,8 @@
r.append(p.getName());
}
if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName())
- && Objects.equals(bp.perm.className, p.className)) {
- bp.protectionLevel = p.protectionLevel;
+ && Objects.equals(bp.perm.getName(), p.getName())) {
+ bp.protectionLevel = p.getProtectionLevel();
}
if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
Log.d(TAG, " Permissions: " + r);
@@ -572,9 +565,9 @@
if (type == BasePermission.TYPE_DYNAMIC) {
if (perm != null || pendingPermissionInfo != null) {
serializer.attribute(null, "type", "dynamic");
- int icon = perm != null ? perm.icon : pendingPermissionInfo.icon;
+ int icon = perm != null ? perm.getIcon() : pendingPermissionInfo.icon;
CharSequence nonLocalizedLabel = perm != null
- ? perm.nonLocalizedLabel
+ ? perm.getNonLocalizedLabel()
: pendingPermissionInfo.nonLocalizedLabel;
if (icon != 0) {
@@ -602,11 +595,11 @@
}
private static boolean comparePermissionInfos(ParsedPermission pi1, PermissionInfo pi2) {
- if (pi1.icon != pi2.icon) return false;
- if (pi1.logo != pi2.logo) return false;
- if (pi1.protectionLevel != pi2.protectionLevel) return false;
+ if (pi1.getIcon() != pi2.icon) return false;
+ if (pi1.getLogo() != pi2.logo) return false;
+ if (pi1.getProtectionLevel() != pi2.protectionLevel) return false;
if (!compareStrings(pi1.getName(), pi2.name)) return false;
- if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
+ if (!compareStrings(pi1.getNonLocalizedLabel(), pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.getPackageName(), pi2.packageName)) return false;
// These are not currently stored in settings.
@@ -644,9 +637,9 @@
pw.println(PermissionInfo.protectionToString(protectionLevel));
if (perm != null) {
pw.print(" perm="); pw.println(perm);
- if ((perm.flags & PermissionInfo.FLAG_INSTALLED) == 0
- || (perm.flags & PermissionInfo.FLAG_REMOVED) != 0) {
- pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.flags));
+ if ((perm.getFlags() & PermissionInfo.FLAG_INSTALLED) == 0
+ || (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0) {
+ pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.getFlags()));
}
}
if (sourcePackageSetting != null) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 36697f9..980aaed 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -71,10 +71,8 @@
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
-import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.Binder;
@@ -129,6 +127,8 @@
import com.android.server.pm.PackageSetting;
import com.android.server.pm.SharedUserSetting;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultBrowserProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider;
import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider;
@@ -1282,7 +1282,7 @@
}
if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
- pkg.toAppInfoWithoutState(), UserHandle.of(userId), permName)
+ pkg.toAppInfoWithoutState(), pkg, UserHandle.of(userId), permName)
.mayGrantPermission()) {
Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
+ packageName);
@@ -2082,9 +2082,9 @@
for (int i = 0; i < numOldPackagePermissions; i++) {
final ParsedPermission permission = oldPackage.getPermissions().get(i);
- if (permission.parsedPermissionGroup != null) {
+ if (permission.getParsedPermissionGroup() != null) {
oldPermissionNameToGroupName.put(permission.getName(),
- permission.parsedPermissionGroup.getName());
+ permission.getParsedPermissionGroup().getName());
}
}
@@ -2098,8 +2098,9 @@
if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
final String permissionName = newPermission.getName();
- final String newPermissionGroupName = newPermission.parsedPermissionGroup == null
- ? null : newPermission.parsedPermissionGroup.getName();
+ final String newPermissionGroupName =
+ newPermission.getParsedPermissionGroup() == null
+ ? null : newPermission.getParsedPermissionGroup().getName();
final String oldPermissionGroupName = oldPermissionNameToGroupName.get(
permissionName);
@@ -2144,7 +2145,7 @@
ParsedPermission p = pkg.getPermissions().get(i);
// Assume by default that we did not install this permission into the system.
- p.flags &= ~PermissionInfo.FLAG_INSTALLED;
+ p.setFlags(p.getFlags() & ~PermissionInfo.FLAG_INSTALLED);
synchronized (PermissionManagerService.this.mLock) {
// Now that permission groups have a special meaning, we ignore permission
@@ -2152,16 +2153,16 @@
// permissions for one app being granted to someone just because they happen
// to be in a group defined by another app (before this had no implications).
if (pkg.getTargetSdkVersion() > Build.VERSION_CODES.LOLLIPOP_MR1) {
- p.parsedPermissionGroup = mSettings.mPermissionGroups.get(p.getGroup());
+ p.setParsedPermissionGroup(mSettings.mPermissionGroups.get(p.getGroup()));
// Warn for a permission in an unknown group.
if (DEBUG_PERMISSIONS
- && p.getGroup() != null && p.parsedPermissionGroup == null) {
+ && p.getGroup() != null && p.getParsedPermissionGroup() == null) {
Slog.i(TAG, "Permission " + p.getName() + " from package "
+ p.getPackageName() + " in an unknown group " + p.getGroup());
}
}
- if (p.tree) {
+ if (p.isTree()) {
final BasePermission bp = BasePermission.createOrUpdate(
mPackageManagerInt,
mSettings.getPermissionTreeLocked(p.getName()), p, pkg,
@@ -2409,14 +2410,16 @@
}
}
+ // TODO(b/140256621): The package instant app method has been removed
+ // as part of work in b/135203078, so this has been commented out in the meantime
// Limit ephemeral apps to ephemeral allowed permissions.
- if (pkg.isInstantApp() && !bp.isInstant()) {
- if (DEBUG_PERMISSIONS) {
- Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
- + " for package " + pkg.getPackageName());
- }
- continue;
- }
+// if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) {
+// if (DEBUG_PERMISSIONS) {
+// Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+// + " for package " + pkg.getPackageName());
+// }
+// continue;
+// }
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
@@ -2450,7 +2453,7 @@
}
} else if (bp.isSignature()) {
// For all apps signature permissions are install time ones.
- allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+ allowedSig = grantSignaturePermission(perm, pkg, ps, bp, origPermissions);
if (allowedSig) {
grant = GRANT_INSTALL;
}
@@ -2764,7 +2767,8 @@
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + pkg.getPackageName()
+ " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x" + Integer.toHexString(pkg.getFlags())
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
+ ")");
}
} else if (bp.isAppOp()) {
@@ -2776,7 +2780,8 @@
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + pkg.getPackageName()
+ " (protectionLevel=" + bp.getProtectionLevel()
- + " flags=0x" + Integer.toHexString(pkg.getFlags())
+ + " flags=0x"
+ + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
+ ")");
}
}
@@ -2784,7 +2789,7 @@
}
if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
- !ps.isSystem() || ps.isUpdatedSystem()) {
+ !ps.isSystem() || !ps.getPkgState().isUpdatedSystemApp()) {
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
@@ -2934,7 +2939,7 @@
*/
private @NonNull int[] checkIfLegacyStorageOpsNeedToBeUpdated(
@NonNull AndroidPackage pkg, boolean replace, @NonNull int[] updatedUserIds) {
- if (replace && pkg.hasRequestedLegacyExternalStorage() && (
+ if (replace && pkg.isRequestLegacyExternalStorage() && (
pkg.getRequestedPermissions().contains(READ_EXTERNAL_STORAGE)
|| pkg.getRequestedPermissions().contains(WRITE_EXTERNAL_STORAGE))) {
return UserManagerService.getInstance().getUserIds();
@@ -3127,7 +3132,7 @@
}
private boolean grantSignaturePermission(String perm, AndroidPackage pkg,
- BasePermission bp, PermissionsState origPermissions) {
+ PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) {
boolean oemPermission = bp.isOEM();
boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
@@ -3139,7 +3144,7 @@
&& !platformPackage && platformPermission) {
if (!hasPrivappWhitelistEntry(perm, pkg)) {
// Only report violations for apps on system image
- if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
+ if (!mSystemReady && !pkgSetting.getPkgState().isUpdatedSystemApp()) {
// it's only a reportable violation if the permission isn't explicitly denied
ArraySet<String> deniedPermissions = null;
if (pkg.isVendor()) {
@@ -3205,8 +3210,8 @@
if (pkg.isSystem()) {
// For updated system applications, a privileged/oem permission
// is granted only if it had been defined by the original application.
- if (pkg.isUpdatedSystemApp()) {
- final PackageSetting disabledPs = (PackageSetting) mPackageManagerInt
+ if (pkgSetting.getPkgState().isUpdatedSystemApp()) {
+ final PackageSetting disabledPs = mPackageManagerInt
.getDisabledSystemPackage(pkg.getPackageName());
final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
if (disabledPs != null
@@ -3612,7 +3617,7 @@
return EmptyArray.INT;
}
for (AndroidPackage pkg : pkgList) {
- if (pkg.getRequestedPermissions() == null) {
+ if (pkg.getRequestedPermissions().isEmpty()) {
continue;
}
final int requestedPermCount = pkg.getRequestedPermissions().size();
@@ -3720,9 +3725,9 @@
// Only system declares background permissions, hence mapping does never change.
mBackgroundPermissions = new ArrayMap<>();
for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
- if (bp.perm != null && bp.perm.backgroundPermission != null) {
+ if (bp.perm != null && bp.perm.getBackgroundPermission() != null) {
String fgPerm = bp.name;
- String bgPerm = bp.perm.backgroundPermission;
+ String bgPerm = bp.perm.getBackgroundPermission();
List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
if (fgPerms == null) {
@@ -4234,7 +4239,7 @@
if (pkg == null) {
return StorageManager.UUID_PRIVATE_INTERNAL;
}
- if (pkg.isExternal()) {
+ if (pkg.isExternalStorage()) {
if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
return StorageManager.UUID_PRIMARY_PHYSICAL;
} else {
@@ -4246,7 +4251,7 @@
}
private static boolean hasPermission(AndroidPackage pkg, String permName) {
- if (pkg.getPermissions() == null) {
+ if (pkg.getPermissions().isEmpty()) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 58a9f42..048e487 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -22,9 +22,10 @@
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.AndroidPackage;
import android.permission.PermissionManagerInternal;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index 254b720..355e243 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.parsing.ComponentParseUtils;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -65,7 +65,7 @@
* name to permission group object.
*/
@GuardedBy("mLock")
- final ArrayMap<String, ComponentParseUtils.ParsedPermissionGroup> mPermissionGroups =
+ final ArrayMap<String, ParsedPermissionGroup> mPermissionGroups =
new ArrayMap<>();
/**
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
new file mode 100644
index 0000000..c008d93
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.pm.pkg;
+
+import static java.util.Collections.emptyList;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.SharedLibraryInfo;
+
+import com.android.internal.util.DataClass;
+import com.android.server.pm.PackageSetting;
+
+import java.util.List;
+
+/**
+ * For use by {@link PackageSetting} to maintain functionality that used to exist in
+ * {@link PackageParser.Package}.
+ *
+ * It is assumed that anything inside the package was not cached or written to disk, so none of
+ * these fields are either. They must be set on every boot from other state on the device.
+ */
+@DataClass(genSetters = true, genConstructor = false, genBuilder = false)
+public class PackageStateUnserialized {
+
+ private boolean hiddenUntilInstalled;
+
+ @NonNull
+ private List<SharedLibraryInfo> usesLibraryInfos = emptyList();
+
+ @NonNull
+ private List<String> usesLibraryFiles = emptyList();
+
+ private boolean updatedSystemApp;
+
+ @NonNull
+ private volatile long[] lastPackageUsageTimeInMills;
+
+ @Nullable
+ private String overrideSeInfo;
+
+ private long[] lazyInitLastPackageUsageTimeInMills() {
+ return new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
+ }
+
+ public PackageStateUnserialized setLastPackageUsageTimeInMills(int reason, long time) {
+ getLastPackageUsageTimeInMills()[reason] = time;
+ return this;
+ }
+
+ public long getLatestPackageUseTimeInMills() {
+ long latestUse = 0L;
+ for (long use : getLastPackageUsageTimeInMills()) {
+ latestUse = Math.max(latestUse, use);
+ }
+ return latestUse;
+ }
+
+ public long getLatestForegroundPackageUseTimeInMills() {
+ int[] foregroundReasons = {
+ PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY,
+ PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE
+ };
+
+ long latestUse = 0L;
+ for (int reason : foregroundReasons) {
+ latestUse = Math.max(latestUse, getLastPackageUsageTimeInMills()[reason]);
+ }
+ return latestUse;
+ }
+
+
+
+ // Code below generated by codegen v1.0.14.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ public boolean isHiddenUntilInstalled() {
+ return hiddenUntilInstalled;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull List<SharedLibraryInfo> getUsesLibraryInfos() {
+ return usesLibraryInfos;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull List<String> getUsesLibraryFiles() {
+ return usesLibraryFiles;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isUpdatedSystemApp() {
+ return updatedSystemApp;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull long[] getLastPackageUsageTimeInMills() {
+ long[] _lastPackageUsageTimeInMills = lastPackageUsageTimeInMills;
+ if (_lastPackageUsageTimeInMills == null) {
+ synchronized(this) {
+ _lastPackageUsageTimeInMills = lastPackageUsageTimeInMills;
+ if (_lastPackageUsageTimeInMills == null) {
+ _lastPackageUsageTimeInMills = lastPackageUsageTimeInMills = lazyInitLastPackageUsageTimeInMills();
+ }
+ }
+ }
+ return _lastPackageUsageTimeInMills;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getOverrideSeInfo() {
+ return overrideSeInfo;
+ }
+
+ @DataClass.Generated.Member
+ public PackageStateUnserialized setHiddenUntilInstalled(boolean value) {
+ hiddenUntilInstalled = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public PackageStateUnserialized setUsesLibraryInfos(@NonNull List<SharedLibraryInfo> value) {
+ usesLibraryInfos = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, usesLibraryInfos);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public PackageStateUnserialized setUsesLibraryFiles(@NonNull List<String> value) {
+ usesLibraryFiles = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, usesLibraryFiles);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public PackageStateUnserialized setUpdatedSystemApp(boolean value) {
+ updatedSystemApp = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public PackageStateUnserialized setLastPackageUsageTimeInMills(@NonNull long... value) {
+ lastPackageUsageTimeInMills = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, lastPackageUsageTimeInMills);
+ return this;
+ }
+
+ @DataClass.Generated.Member
+ public PackageStateUnserialized setOverrideSeInfo(@Nullable String value) {
+ overrideSeInfo = value;
+ return this;
+ }
+
+ @DataClass.Generated(
+ time = 1580422870209L,
+ codegenVersion = "1.0.14",
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java",
+ inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\n @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index ec8e1a0..139c844 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -39,7 +39,6 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -65,6 +64,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
@@ -163,7 +163,7 @@
if (perm.isSoftRestricted()) {
SoftRestrictedPermissionPolicy policy =
SoftRestrictedPermissionPolicy.forPermission(null, null, null,
- perm.name);
+ null, perm.name);
int extraAppOp = policy.getExtraAppOpCode();
if (extraAppOp != OP_NONE) {
appOpsService.startWatchingMode(extraAppOp, null, mAppOpsCallback);
@@ -506,17 +506,18 @@
/**
* Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
*/
- private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull String permissionName) {
+ private void addAppOps(@NonNull PackageInfo packageInfo, @NonNull AndroidPackage pkg,
+ @NonNull String permissionName) {
PermissionInfo permissionInfo = mRuntimePermissionInfos.get(permissionName);
if (permissionInfo == null) {
return;
}
- addPermissionAppOp(packageInfo, permissionInfo);
- addExtraAppOp(packageInfo, permissionInfo);
+ addPermissionAppOp(packageInfo, pkg, permissionInfo);
+ addExtraAppOp(packageInfo, pkg, permissionInfo);
}
private void addPermissionAppOp(@NonNull PackageInfo packageInfo,
- @NonNull PermissionInfo permissionInfo) {
+ @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo) {
if (!permissionInfo.isRuntime()) {
return;
}
@@ -539,13 +540,13 @@
}
int appOpMode;
- boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, permissionInfo);
+ boolean shouldGrantAppOp = shouldGrantAppOp(packageInfo, pkg, permissionInfo);
if (shouldGrantAppOp) {
if (permissionInfo.backgroundPermission != null) {
PermissionInfo backgroundPermissionInfo = mRuntimePermissionInfos.get(
permissionInfo.backgroundPermission);
boolean shouldGrantBackgroundAppOp = backgroundPermissionInfo != null
- && shouldGrantAppOp(packageInfo, backgroundPermissionInfo);
+ && shouldGrantAppOp(packageInfo, pkg, backgroundPermissionInfo);
appOpMode = shouldGrantBackgroundAppOp ? MODE_ALLOWED : MODE_FOREGROUND;
} else {
appOpMode = MODE_ALLOWED;
@@ -570,7 +571,7 @@
}
private boolean shouldGrantAppOp(@NonNull PackageInfo packageInfo,
- @NonNull PermissionInfo permissionInfo) {
+ @NonNull AndroidPackage pkg, @NonNull PermissionInfo permissionInfo) {
String permissionName = permissionInfo.name;
String packageName = packageInfo.packageName;
boolean isGranted = mPackageManager.checkPermission(permissionName, packageName)
@@ -595,14 +596,15 @@
} else if (permissionInfo.isSoftRestricted()) {
SoftRestrictedPermissionPolicy policy =
SoftRestrictedPermissionPolicy.forPermission(mContext,
- packageInfo.applicationInfo, mContext.getUser(), permissionName);
+ packageInfo.applicationInfo, pkg, mContext.getUser(),
+ permissionName);
return policy.mayGrantPermission();
} else {
return true;
}
}
- private void addExtraAppOp(@NonNull PackageInfo packageInfo,
+ private void addExtraAppOp(@NonNull PackageInfo packageInfo, @NonNull AndroidPackage pkg,
@NonNull PermissionInfo permissionInfo) {
if (!permissionInfo.isSoftRestricted()) {
return;
@@ -611,7 +613,7 @@
String permissionName = permissionInfo.name;
SoftRestrictedPermissionPolicy policy =
SoftRestrictedPermissionPolicy.forPermission(mContext,
- packageInfo.applicationInfo, mContext.getUser(), permissionName);
+ packageInfo.applicationInfo, pkg, mContext.getUser(), permissionName);
int extraOpCode = policy.getExtraAppOpCode();
if (extraOpCode == OP_NONE) {
return;
@@ -639,19 +641,23 @@
* @param pkgName The package to add for later processing.
*/
void addPackage(@NonNull String pkgName) {
- final PackageInfo pkg;
+ PackageManagerInternal pmInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ final PackageInfo pkgInfo;
+ final AndroidPackage pkg;
try {
- pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
+ pkgInfo = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
+ pkg = pmInternal.getPackage(pkgName);
} catch (NameNotFoundException e) {
return;
}
- if (pkg.requestedPermissions == null) {
+ if (pkgInfo == null || pkg == null || pkgInfo.requestedPermissions == null) {
return;
}
- for (String permission : pkg.requestedPermissions) {
- addAppOps(pkg, permission);
+ for (String permission : pkgInfo.requestedPermissions) {
+ addAppOps(pkgInfo, pkg, permission);
}
}
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 740472e..90babcd 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -35,7 +35,6 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -46,6 +45,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
/**
* The behavior of soft restricted permissions is different for each permission. This class collects
@@ -100,8 +100,8 @@
* @return The policy for this permission
*/
public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
- @Nullable ApplicationInfo appInfo, @Nullable UserHandle user,
- @NonNull String permission) {
+ @Nullable ApplicationInfo appInfo, @Nullable AndroidPackage pkg,
+ @Nullable UserHandle user, @NonNull String permission) {
switch (permission) {
// Storage uses a special app op to decide the mount state and supports soft restriction
// where the restricted state allows the permission but only for accessing the medial
@@ -116,8 +116,6 @@
if (appInfo != null) {
PackageManager pm = context.getPackageManager();
- PackageManagerInternal pmInternal =
- LocalServices.getService(PackageManagerInternal.class);
StorageManagerInternal smInternal =
LocalServices.getService(StorageManagerInternal.class);
int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
@@ -131,8 +129,7 @@
isScopedStorageEnabled =
isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
|| isScopedStorageRequired;
- shouldPreserveLegacyExternalStorage = pmInternal.getPackage(
- appInfo.packageName).hasPreserveLegacyExternalStorage()
+ shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage()
&& smInternal.hasLegacyExternalStorage(appInfo.uid);
shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
|| (isScopedStorageRequired && !shouldPreserveLegacyExternalStorage);
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 9f4ca3c..97ce6bd 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -364,12 +364,12 @@
(Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked());
}
- mPersistence.write(roles, UserHandle.of(mUserId));
+ mPersistence.writeAsUser(roles, UserHandle.of(mUserId));
}
private void readFile() {
synchronized (mLock) {
- RolesState roles = mPersistence.read(UserHandle.of(mUserId));
+ RolesState roles = mPersistence.readAsUser(UserHandle.of(mUserId));
if (roles == null) {
readLegacyFileLocked();
scheduleWriteFileLocked();
@@ -545,7 +545,7 @@
throw new IllegalStateException("This RoleUserState has already been destroyed");
}
mWriteHandler.removeCallbacksAndMessages(null);
- mPersistence.delete(UserHandle.of(mUserId));
+ mPersistence.deleteAsUser(UserHandle.of(mUserId));
mDestroyed = true;
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
new file mode 100644
index 0000000..870d909
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 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 com.android.server.soundtrigger_middleware;
+
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHw;
+import android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback;
+import android.hardware.soundtrigger.V2_3.ModelParameterRange;
+import android.hardware.soundtrigger.V2_3.Properties;
+import android.hardware.soundtrigger.V2_3.RecognitionConfig;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A decorator around a HAL, which adds some checks that the HAL is behaving as expected.
+ * This is not necessarily a strict enforcement for the HAL contract, but a place to add checks for
+ * common HAL malfunctions, to help track them and assist in debugging.
+ *
+ * The class is not thread-safe.
+ */
+public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
+ static final String TAG = "SoundTriggerHw2Enforcer";
+
+ final ISoundTriggerHw2 mUnderlying;
+ Map<Integer, Boolean> mModelStates = new HashMap<>();
+
+ public SoundTriggerHw2Enforcer(
+ ISoundTriggerHw2 underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mUnderlying.getProperties();
+ }
+
+ @Override
+ public int loadSoundModel(ISoundTriggerHw.SoundModel soundModel, Callback callback,
+ int cookie) {
+ int handle = mUnderlying.loadSoundModel(soundModel, new CallbackEnforcer(callback), cookie);
+ mModelStates.put(handle, false);
+ return handle;
+ }
+
+ @Override
+ public int loadPhraseSoundModel(ISoundTriggerHw.PhraseSoundModel soundModel, Callback callback,
+ int cookie) {
+ int handle = mUnderlying.loadPhraseSoundModel(soundModel, new CallbackEnforcer(callback),
+ cookie);
+ mModelStates.put(handle, false);
+ return handle;
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ mUnderlying.unloadSoundModel(modelHandle);
+ mModelStates.remove(modelHandle);
+ }
+
+ @Override
+ public void stopRecognition(int modelHandle) {
+ mUnderlying.stopRecognition(modelHandle);
+ mModelStates.replace(modelHandle, false);
+ }
+
+ @Override
+ public void stopAllRecognitions() {
+ mUnderlying.stopAllRecognitions();
+ for (Map.Entry<Integer, Boolean> entry : mModelStates.entrySet()) {
+ entry.setValue(false);
+ }
+ }
+
+ @Override
+ public void startRecognition(int modelHandle, RecognitionConfig config, Callback callback,
+ int cookie) {
+ mUnderlying.startRecognition(modelHandle, config, new CallbackEnforcer(callback), cookie);
+ mModelStates.replace(modelHandle, true);
+ }
+
+ @Override
+ public void getModelState(int modelHandle) {
+ mUnderlying.getModelState(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mUnderlying.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mUnderlying.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mUnderlying.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+ return mUnderlying.linkToDeath(recipient, cookie);
+ }
+
+ @Override
+ public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+ return mUnderlying.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() throws RemoteException {
+ return mUnderlying.interfaceDescriptor();
+ }
+
+ private class CallbackEnforcer implements Callback {
+ private final Callback mUnderlying;
+
+ private CallbackEnforcer(
+ Callback underlying) {
+ mUnderlying = underlying;
+ }
+
+ @Override
+ public void recognitionCallback(ISoundTriggerHwCallback.RecognitionEvent event,
+ int cookie) {
+ int model = event.header.model;
+ if (!mModelStates.getOrDefault(model, false)) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ }
+ if (event.header.status
+ != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+ mModelStates.replace(model, false);
+ }
+ mUnderlying.recognitionCallback(event, cookie);
+ }
+
+ @Override
+ public void phraseRecognitionCallback(ISoundTriggerHwCallback.PhraseRecognitionEvent event,
+ int cookie) {
+ int model = event.common.header.model;
+ if (!mModelStates.getOrDefault(model, false)) {
+ Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ }
+ if (event.common.header.status
+ != android.media.soundtrigger_middleware.RecognitionStatus.FORCED) {
+ mModelStates.replace(model, false);
+ }
+ mUnderlying.phraseRecognitionCallback(event, cookie);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index cd5e360..aa1558e 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -191,7 +191,7 @@
* Attached to the HAL service via factory.
*/
private void attachToHal() {
- mHalService = new SoundTriggerHw2Compat(mHalFactory.create());
+ mHalService = new SoundTriggerHw2Enforcer(new SoundTriggerHw2Compat(mHalFactory.create()));
mHalService.linkToDeath(this, 0);
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 2b111f7..df6784e 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -41,6 +41,7 @@
import android.app.AppOpsManager.HistoricalUidOps;
import android.app.INotificationManager;
import android.app.ProcessMemoryState;
+import android.app.RuntimeAppOpAccessMessage;
import android.app.StatsManager;
import android.app.StatsManager.PullAtomMetadata;
import android.bluetooth.BluetoothActivityEnergyInfo;
@@ -377,6 +378,8 @@
return pullFaceSettings(atomTag, data);
case FrameworkStatsLog.APP_OPS:
return pullAppOps(atomTag, data);
+ case FrameworkStatsLog.RUNTIME_APP_OP_ACCESS:
+ return pullRuntimeAppOpAccessMessage(atomTag, data);
case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS:
return pullNotificationRemoteViews(atomTag, data);
case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
@@ -539,6 +542,7 @@
registerAppsOnExternalStorageInfo();
registerFaceSettings();
registerAppOps();
+ registerRuntimeAppOpAccessMessage();
registerNotificationRemoteViews();
registerDangerousPermissionState();
registerDangerousPermissionStateSampled();
@@ -2834,6 +2838,17 @@
}
+ private void registerRuntimeAppOpAccessMessage() {
+ int tagId = FrameworkStatsLog.RUNTIME_APP_OP_ACCESS;
+ mStatsManager.registerPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(),
+ mStatsCallbackImpl
+ );
+
+ }
+
int pullAppOps(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
@@ -2894,6 +2909,41 @@
return StatsManager.PULL_SUCCESS;
}
+ int pullRuntimeAppOpAccessMessage(int atomTag, List<StatsEvent> pulledData) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+
+ RuntimeAppOpAccessMessage message = appOps.collectRuntimeAppOpAccessMessage();
+ if (message == null) {
+ Slog.i(TAG, "No runtime appop access message collected");
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ StatsEvent.Builder e = StatsEvent.newBuilder();
+ e.setAtomId(atomTag);
+ e.writeInt(message.getUid());
+ e.writeString(message.getPackageName());
+ e.writeString(message.getOp());
+ if (message.getFeatureId() == null) {
+ e.writeString("");
+ } else {
+ e.writeString(message.getFeatureId());
+ }
+ e.writeString(message.getMessage());
+ e.writeInt(message.getSamplingStrategy());
+
+ pulledData.add(e.build());
+ } catch (Throwable t) {
+ // TODO: catch exceptions at a more granular level
+ Slog.e(TAG, "Could not read runtime appop access message", t);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
static void unpackStreamedData(int atomTag, List<StatsEvent> pulledData,
List<ParcelFileDescriptor> statsFiles) throws IOException {
InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4cc4851..04fae97 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1163,8 +1163,12 @@
// An activity is considered to be in multi-window mode if its task isn't fullscreen.
final boolean inMultiWindowMode = inMultiWindowMode();
if (inMultiWindowMode != mLastReportedMultiWindowMode) {
- mLastReportedMultiWindowMode = inMultiWindowMode;
- scheduleMultiWindowModeChanged(getConfiguration());
+ if (!inMultiWindowMode && mLastReportedPictureInPictureMode) {
+ updatePictureInPictureMode(null, false);
+ } else {
+ mLastReportedMultiWindowMode = inMultiWindowMode;
+ scheduleMultiWindowModeChanged(getConfiguration());
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 688f474..2f1cc05 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -658,8 +658,8 @@
}
@Override
- public void resolveOverrideConfiguration(Configuration newParentConfig) {
- super.resolveOverrideConfiguration(newParentConfig);
+ public void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+ super.resolveTileOverrideConfiguration(newParentConfig);
if (mTile != null) {
// If this is a virtual child of a tile, simulate the parent-child relationship
mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
@@ -742,8 +742,8 @@
setBounds(newBounds);
} else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
// For pinned stack, resize is now part of the {@link WindowContainerTransaction}
- resize(new Rect(newBounds), null /* tempTaskBounds */,
- null /* tempTaskInsetBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+ resize(new Rect(newBounds), null /* configBounds */,
+ PRESERVE_WINDOWS, true /* deferResume */);
}
}
if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -952,8 +952,8 @@
}
if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
- resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- false /* preserveWindows */, true /* deferResume */);
+ resize(mTmpRect2, null /*configBounds*/,
+ false /*preserveWindows*/, true /*deferResume*/);
}
} finally {
if (showRecents && !alreadyInSplitScreenMode && isOnHomeDisplay()
@@ -1118,20 +1118,15 @@
return r.getTask().mTaskId != taskId && r.appToken != notTop && r.canBeTopRunning();
}
- ActivityRecord isInStackLocked(IBinder token) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- return isInStackLocked(r);
- }
-
ActivityRecord isInStackLocked(ActivityRecord r) {
if (r == null) {
return null;
}
- final Task task = r.getTask();
- final ActivityStack stack = r.getRootTask();
- if (stack != null && task.mChildren.contains(r) && mChildren.contains(task)) {
- if (stack != this) Slog.w(TAG,
- "Illegal state! task does not point to stack it is in.");
+ final Task task = r.getRootTask();
+ if (task != null && r.isDescendantOf(task)) {
+ if (task != this) Slog.w(TAG, "Illegal state! task does not point to stack it is in. "
+ + "stack=" + this + " task=" + task + " r=" + r
+ + " callers=" + Debug.getCallers(15, "\n"));
return r;
}
return null;
@@ -1207,7 +1202,7 @@
}
getDisplay().positionStackAtBottom(this, reason);
- if (task != null) {
+ if (task != null && task != this) {
positionChildAtBottom(task);
}
@@ -1251,12 +1246,12 @@
mCurrentUser = userId;
super.switchUser(userId);
- forAllTasks((t) -> {
- if (t.showToCurrentUser()) {
+ forAllLeafTasks((t) -> {
+ if (t.showToCurrentUser() && t != this) {
mChildren.remove(t);
mChildren.add(t);
}
- }, true /* traverseTopToBottom */, this);
+ }, true /* traverseTopToBottom */);
}
void minimalResumeActivityLocked(ActivityRecord r) {
@@ -2450,16 +2445,16 @@
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
Task rTask = r.getTask();
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
- final boolean hasTask = hasChild(rTask);
+ final boolean isOrhasTask = rTask == this || hasChild(rTask);
// mLaunchTaskBehind tasks get placed at the back of the task stack.
- if (!r.mLaunchTaskBehind && allowMoveToFront && (!hasTask || newTask)) {
+ if (!r.mLaunchTaskBehind && allowMoveToFront && (!isOrhasTask || newTask)) {
// Last activity in task had been removed or ActivityManagerService is reusing task.
// Insert or replace.
// Might not even be in.
positionChildAtTop(rTask);
}
Task task = null;
- if (!newTask && hasTask) {
+ if (!newTask && isOrhasTask) {
final ActivityRecord occludingActivity = getActivity(
(ar) -> !ar.finishing && ar.occludesParent(), true, rTask);
if (occludingActivity != null) {
@@ -2717,7 +2712,7 @@
void finishVoiceTask(IVoiceInteractionSession session) {
final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::finishIfVoiceTask,
PooledLambda.__(Task.class), session.asBinder());
- forAllTasks(c, true /* traverseTopToBottom */, this);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
@@ -2818,8 +2813,7 @@
return false;
}
final Task task = srec.getTask();
-
- if (!mChildren.contains(task) || !task.hasChild(srec)) {
+ if (!srec.isDescendantOf(this)) {
return false;
}
@@ -2932,20 +2926,20 @@
getDisplay().mDisplayContent.prepareAppTransition(transit, false);
}
- final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+ final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
- moveTaskToFrontLocked(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
+ moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
}
- final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+ final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, boolean deferResume, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
- final ActivityRecord topActivity = topStack != null ? topStack.getTopNonFinishingActivity() : null;
- final int numTasks = getChildCount();
- final int index = mChildren.indexOf(tr);
- if (numTasks == 0 || index < 0) {
+ final ActivityRecord topActivity = topStack != null
+ ? topStack.getTopNonFinishingActivity() : null;
+
+ if (tr != this && !tr.isDescendantOf(this)) {
// nothing to do!
if (noAnimation) {
ActivityOptions.abort(options);
@@ -3099,24 +3093,30 @@
// TODO: Can only be called from special methods in ActivityStackSupervisor.
// Need to consolidate those calls points into this resize method so anyone can call directly.
- void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
- boolean preserveWindows, boolean deferResume) {
- if (!updateBoundsAllowed(bounds)) {
+ void resize(Rect displayedBounds, Rect configBounds, boolean preserveWindows,
+ boolean deferResume) {
+ if (!updateBoundsAllowed(displayedBounds)) {
return;
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + getRootTaskId());
mAtmService.deferWindowLayout();
try {
+ // TODO: Why not just set this on the stack directly vs. on each tasks?
// Update override configurations of all tasks in the stack.
- final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityStack::processTaskResizeBounds, PooledLambda.__(Task.class),
- taskBounds, tempTaskInsetBounds);
- forAllTasks(c, true /* traverseTopToBottom */, this);
+ displayedBounds, configBounds);
+ forAllTasks(c, true /* traverseTopToBottom */);
c.recycle();
- setBounds(bounds);
+ if (mBoundsAnimating) {
+ // Force to update task surface bounds and relayout windows, since configBounds
+ // remains unchanged during bounds animation.
+ updateSurfaceBounds();
+ getDisplay().setLayoutNeeded();
+ mWmService.requestTraversal();
+ }
if (!deferResume) {
ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
@@ -3127,15 +3127,16 @@
}
}
- private static void processTaskResizeBounds(Task task, Rect bounds, Rect insetBounds) {
+ private static void processTaskResizeBounds(
+ Task task, Rect displayedBounds, Rect configBounds) {
if (!task.isResizeable()) return;
- if (insetBounds != null && !insetBounds.isEmpty()) {
- task.setOverrideDisplayedBounds(bounds);
- task.setBounds(insetBounds);
+ if (configBounds != null && !configBounds.isEmpty()) {
+ task.setOverrideDisplayedBounds(displayedBounds);
+ task.setBounds(configBounds);
} else {
task.setOverrideDisplayedBounds(null);
- task.setBounds(bounds);
+ task.setBounds(displayedBounds);
}
}
@@ -3150,7 +3151,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskBounds,
PooledLambda.__(Task.class), bounds);
- forAllTasks(c, true /* traverseTopToBottom */, this);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
@@ -3166,7 +3167,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::setTaskDisplayedBounds,
PooledLambda.__(Task.class), bounds);
- forAllTasks(c, true /* traverseTopToBottom */, this);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
@@ -3262,7 +3263,7 @@
return false;
}
final String prefix = " ";
- forAllTasks((task) -> {
+ forAllLeafTasks((task) -> {
if (needSep) {
pw.println("");
}
@@ -3280,7 +3281,7 @@
false /* traverseTopToBottom */);
dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
dumpPackage, false, null, task);
- }, true /* traverseTopToBottom */, this);
+ }, true /* traverseTopToBottom */);
return true;
}
@@ -3331,19 +3332,33 @@
}
}
- Task createTask(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
- return createTask(taskId, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
+ Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
+ return reuseOrCreateTask(info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
toTop, null /*activity*/, null /*source*/, null /*options*/);
}
+ // TODO: Can be removed once we change callpoints creating stacks to be creating tasks.
+ /** Either returns this current task to be re-used or creates a new child task. */
+ Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
+ IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity,
+ ActivityRecord source, ActivityOptions options) {
- Task createTask(int taskId, ActivityInfo info, Intent intent,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- boolean toTop, ActivityRecord activity, ActivityRecord source,
- ActivityOptions options) {
- final Task task = Task.create(
- mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this);
- // add the task to stack first, mTaskPositioner might need the stack association
- addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+ Task task;
+ if (DisplayContent.alwaysCreateStack(getWindowingMode(), getActivityType())) {
+ // This stack will only contain one task, so just return itself since all stacks ara now
+ // tasks and all tasks are now stacks.
+ task = reuseAsLeafTask(voiceSession, voiceInteractor, info, activity);
+ } else {
+ // Create child task since this stack can contain multiple tasks.
+ final int taskId = activity != null
+ ? mStackSupervisor.getNextTaskIdForUser(activity.mUserId)
+ : mStackSupervisor.getNextTaskIdForUser();
+ task = Task.create(
+ mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this);
+
+ // add the task to stack first, mTaskPositioner might need the stack association
+ addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+ }
+
int displayId = getDisplayId();
if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
final boolean isLockscreenShown = mAtmService.mStackSupervisor.getKeyguardController()
@@ -3353,6 +3368,7 @@
&& !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
task.setBounds(getRequestedOverrideBounds());
}
+
return task;
}
@@ -3559,10 +3575,6 @@
"Can't exit pinned mode if it's not pinned already.");
}
- if (mChildren.size() != 1) {
- throw new RuntimeException("There should be only one task in a pinned stack.");
- }
-
// give pinned stack a chance to save current bounds, this should happen before reparent.
final ActivityRecord top = topRunningNonOverlayTaskActivity();
if (top != null && top.isVisible()) {
@@ -3592,12 +3604,12 @@
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor,
PooledLambda.__(Task.class), targetStackBounds, forceUpdate);
- forAllTasks(c, true /* traverseTopToBottom */, this);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
void prepareFreezingTaskBounds() {
- forAllTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */, this);
+ forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */);
}
/**
@@ -3629,7 +3641,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(Task::alignToAdjustedBounds,
PooledLambda.__(Task.class), adjusted ? mAdjustedBounds : getRawBounds(),
insetBounds, alignBottom);
- forAllTasks(c, true /* traverseTopToBottom */, this);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
@@ -3902,6 +3914,12 @@
return;
}
+ if (child == this) {
+ // TODO: Fix call-points
+ moveToFront("positionChildAtTop");
+ return;
+ }
+
positionChildAt(POSITION_TOP, child, true /* includingParents */);
child.updateTaskMovement(true);
@@ -4316,19 +4334,19 @@
* to the list of to be drawn windows the service is waiting for.
*/
void beginImeAdjustAnimation() {
- forAllTasks((t) -> {
+ forAllLeafTasks((t) -> {
if (t.hasContentToDisplay()) {
t.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
t.setWaitingForDrawnIfResizingChanged();
}
- }, true /* traverseTopToBottom */, this);
+ }, true /* traverseTopToBottom */);
}
/** Resets the resizing state of all windows. */
void endImeAdjustAnimation() {
- forAllTasks((t) -> {
+ forAllLeafTasks((t) -> {
t.setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
- }, true /* traverseTopToBottom */, this);
+ }, true /* traverseTopToBottom */);
}
private int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
@@ -4572,19 +4590,15 @@
return task != null;
}
- public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
+ public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mWmService.mGlobalLock) {
if (mCancelCurrentBoundsAnimation) {
return false;
}
+ mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
}
- try {
- mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
- } catch (RemoteException e) {
- // I don't believe you.
- }
return true;
}
@@ -4730,7 +4744,7 @@
/** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */
void onPipAnimationEndResize() {
mBoundsAnimating = false;
- forAllTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */, this);
+ forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */);
mWmService.requestTraversal();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 70cd01b..97b6388 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -419,14 +419,29 @@
mTopTask = fromStack.getTopMostTask();
final PooledConsumer c = PooledLambda.obtainConsumer(
- MoveTaskToFullscreenHelper::processTask, this, PooledLambda.__(Task.class));
- fromStack.forAllTasks(c, false /* traverseTopToBottom */, fromStack);
+ MoveTaskToFullscreenHelper::processLeafTask, this, PooledLambda.__(Task.class));
+ fromStack.forAllLeafTasks(c, false /* traverseTopToBottom */);
c.recycle();
mToDisplay = null;
mTopTask = null;
}
- private void processTask(Task task) {
+ private void processLeafTask(Task task) {
+ // This is a one level task that we don't need to create stack for reparenting to.
+ if (task.isRootTask() && DisplayContent.alwaysCreateStack(WINDOWING_MODE_FULLSCREEN,
+ task.getActivityType())) {
+ final ActivityStack stack = (ActivityStack) task;
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ if (mToDisplay.getDisplayId() != stack.getDisplayId()) {
+ mToDisplay.moveStackToDisplay(stack, mOnTop);
+ } else if (mOnTop) {
+ mToDisplay.positionStackAtTop(stack, false /* includingParents */);
+ } else {
+ mToDisplay.positionStackAtBottom(stack);
+ }
+ return;
+ }
+
final ActivityStack toStack = mToDisplay.getOrCreateStack(
null, mTmpOptions, task, task.getActivityType(), mOnTop);
@@ -1428,8 +1443,7 @@
// still need moveTaskToFrontLocked() below for any transition settings.
}
if (stack.shouldResizeStackWithLaunchBounds()) {
- stack.resize(bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- !PRESERVE_WINDOWS, !DEFER_RESUME);
+ stack.resize(bounds, null /* configBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
} else {
// WM resizeTask must be done after the task is moved to the correct stack,
// because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1443,7 +1457,7 @@
}
final ActivityRecord r = task.getTopNonFinishingActivity();
- currentStack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+ currentStack.moveTaskToFront(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
if (DEBUG_STACK) Slog.d(TAG_STACK,
@@ -1589,7 +1603,7 @@
false /* deferResume */);
}
- void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+ void resizeDockedStackLocked(Rect displayedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows, boolean deferResume) {
@@ -1607,7 +1621,7 @@
if (mDockedStackResizing) {
mHasPendingDockedBounds = true;
- mPendingDockedBounds = copyOrNull(dockedBounds);
+ mPendingDockedBounds = copyOrNull(displayedBounds);
mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
@@ -1620,13 +1634,13 @@
// Don't allow re-entry while resizing. E.g. due to docked stack detaching.
mAllowDockedStackResize = false;
ActivityRecord r = stack.topRunningActivity();
- stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+ stack.resize(displayedBounds, tempDockedTaskBounds,
!PRESERVE_WINDOWS, DEFER_RESUME);
// TODO: Checking for isAttached might not be needed as if the user passes in null
// dockedBounds then they want the docked stack to be dismissed.
if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
- || (dockedBounds == null && !stack.isAttached())) {
+ || (displayedBounds == null && !stack.isAttached())) {
// The dock stack either was dismissed or went fullscreen, which is kinda the same.
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
@@ -1654,7 +1668,7 @@
// interaction.
continue;
}
- current.getStackDockedModeBounds(dockedBounds,
+ current.getStackDockedModeBounds(displayedBounds,
tempOtherTaskBounds /* currentTempTaskBounds */,
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */);
@@ -1669,9 +1683,7 @@
+ " non-fullscreen stack");
}
- current.resize(tempRect,
- !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
- tempOtherTaskInsetBounds, preserveWindows, deferResume);
+ current.resize(tempRect, tempOtherTaskBounds, preserveWindows, deferResume);
}
}
if (!deferResume) {
@@ -1684,7 +1696,7 @@
}
}
- void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+ void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) {
// TODO(multi-display): The display containing the stack should be passed in.
final ActivityStack stack =
mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
@@ -1696,23 +1708,22 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
mService.deferWindowLayout();
try {
- Rect insetBounds = null;
- if (tempPinnedTaskBounds != null && stack.isAnimatingBoundsToFullscreen()) {
+ Rect configBounds = null;
+ if (inConfigBounds != null) {
// Use 0,0 as the position for the inset rect because we are headed for fullscreen.
- insetBounds = tempRect;
- insetBounds.top = 0;
- insetBounds.left = 0;
- insetBounds.right = tempPinnedTaskBounds.width();
- insetBounds.bottom = tempPinnedTaskBounds.height();
+ configBounds = tempRect;
+ configBounds.top = 0;
+ configBounds.left = 0;
+ configBounds.right = inConfigBounds.width();
+ configBounds.bottom = inConfigBounds.height();
}
- if (pinnedBounds != null && tempPinnedTaskBounds == null) {
+ if (displayedBounds != null && inConfigBounds == null) {
// We have finished the animation into PiP, and are resizing the tasks to match the
// stack bounds, while layouts are deferred, update any task state as a part of
// transitioning it from fullscreen into a floating state.
stack.onPipAnimationEndResize();
}
- stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds, !PRESERVE_WINDOWS,
- !DEFER_RESUME);
+ stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
} finally {
mService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -1742,7 +1753,7 @@
} else {
final PooledConsumer c = PooledLambda.obtainConsumer(
ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
- stack.forAllTasks(c, true /* traverseTopToBottom */, stack);
+ stack.forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
}
@@ -2755,6 +2766,10 @@
deferUpdateRecentsHomeStackBounds();
// TODO(multi-display): currently recents animation only support default display.
mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
+ // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+ // Unset launching windowing mode to prevent creating split-screen-primary stack
+ // in RWC#anyTaskForId() below.
+ activityOptions.setLaunchWindowingMode(WINDOWING_MODE_UNDEFINED);
}
task = mRootWindowContainer.anyTaskForId(taskId,
@@ -2764,6 +2779,9 @@
mWindowManager.executeAppTransition();
throw new IllegalArgumentException(
"startActivityFromRecents: Task " + taskId + " not found.");
+ } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && task.getWindowingMode() != windowingMode) {
+ mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */);
}
if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2a2ab4b..600a125 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2366,7 +2366,7 @@
// task on top there.
// Defer resuming the top activity while moving task to top, since the
// current task-top activity may not be the activity that should be resumed.
- mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
+ mTargetStack.moveTaskToFront(intentTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, DEFER_RESUME,
"bringingFoundTaskToFront");
mMovedToFront = !isSplitScreenTopStack;
@@ -2396,8 +2396,7 @@
private void setNewTask(Task taskToAffiliate) {
final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
- final Task task = mTargetStack.createTask(
- mSupervisor.getNextTaskIdForUser(mStartActivity.mUserId),
+ final Task task = mTargetStack.reuseOrCreateTask(
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 882d5c7..344d4a5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2760,9 +2760,17 @@
throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
+ if (!task.supportsSplitScreenWindowingMode()) {
+ return false;
+ }
final int prevMode = task.getWindowingMode();
- final ActivityStack stack = task.getStack();
+ moveTaskToSplitScreenPrimaryTile(task, toTop);
+ return prevMode != task.getWindowingMode();
+ }
+
+ void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
+ ActivityStack stack = task.getStack();
TaskTile tile = null;
for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
tile = stack.getDisplay().getStackAt(i).asTile();
@@ -2776,7 +2784,6 @@
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
mTaskOrganizerController.applyContainerTransaction(wct, null);
- return prevMode != task.getWindowingMode();
}
/**
@@ -3247,8 +3254,9 @@
}
final ActivityStack stack = r.getRootTask();
- final Task task = stack.createTask(
- mStackSupervisor.getNextTaskIdForUser(r.mUserId), ainfo, intent, !ON_TOP);
+ final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
+ stack.getActivityType(), !ON_TOP, ainfo, intent);
+
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
stack.removeChild(task, "addAppTask");
@@ -4426,12 +4434,12 @@
}
@Override
- public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+ public void resizePinnedStack(Rect displayedBounds, Rect configBounds) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
+ mStackSupervisor.resizePinnedStack(displayedBounds, configBounds);
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 9f54e49e0..b1d5359 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -52,7 +52,7 @@
* animation is now invalid and not required. In such a case, the cancel will trigger the
* animation end callback as well, but will not send any further size changes.
*/
- boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds);
+ boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds);
/** Sets the alpha of the animation target */
boolean setPinnedStackAlpha(float alpha);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e60ab3f..0029dc8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4415,7 +4415,7 @@
ArrayList<Task> getVisibleTasks() {
final ArrayList<Task> visibleTasks = new ArrayList<>();
forAllTasks(task -> {
- if (!task.isRootTask() && task.isVisible()) {
+ if (task.isLeafTask() && task.isVisible()) {
visibleTasks.add(task);
}
});
@@ -4537,6 +4537,8 @@
true /* includingParents */);
}
+ child.updateTaskMovement(moveToTop);
+
setLayoutNeeded();
}
@@ -5790,7 +5792,7 @@
return null;
}
- boolean alwaysCreateStack(int windowingMode, int activityType) {
+ static boolean alwaysCreateStack(int windowingMode, int activityType) {
// Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
// modes so that we can manage visual ordering and return types correctly.
return activityType == ACTIVITY_TYPE_STANDARD
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e443fe4..f02a9dd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -131,7 +131,6 @@
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.power.V1_0.PowerHint;
-import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -304,6 +303,8 @@
private boolean mIsFreeformWindowOverlappingWithNavBar;
+ private boolean mLastImmersiveMode;
+
private final StatusBarController mStatusBarController;
private final BarController mNavigationBarController;
@@ -3182,8 +3183,8 @@
if (nb) mNavigationBarController.showTransient();
updateSystemUiVisibilityLw();
}
- mImmersiveModeConfirmation.confirmCurrentPrompt();
}
+ mImmersiveModeConfirmation.confirmCurrentPrompt();
}
}
@@ -3210,7 +3211,7 @@
updateSystemUiVisibilityLw();
}
- private int updateSystemUiVisibilityLw() {
+ int updateSystemUiVisibilityLw() {
// If there is no window focused, there will be nobody to handle the events
// anyway, so just hang on in whatever state we're in until things settle down.
WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
@@ -3566,9 +3567,11 @@
vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
// update navigation bar
- boolean oldImmersiveMode = isImmersiveMode(oldVis);
- boolean newImmersiveMode = isImmersiveMode(vis);
+ boolean newInsetsMode = ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL;
+ boolean oldImmersiveMode = newInsetsMode ? mLastImmersiveMode : isImmersiveMode(oldVis);
+ boolean newImmersiveMode = newInsetsMode ? isImmersiveMode(win) : isImmersiveMode(vis);
if (oldImmersiveMode != newImmersiveMode) {
+ mLastImmersiveMode = newImmersiveMode;
final String pkg = win.getOwningPackage();
mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
mService.mPolicy.isUserSetupComplete(),
@@ -3673,6 +3676,7 @@
}
}
+ // TODO(b/118118435): Remove this after migration
private boolean isImmersiveMode(int vis) {
final int flags = View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
return mNavigationBar != null
@@ -3681,6 +3685,16 @@
&& canHideNavigationBar();
}
+ private boolean isImmersiveMode(WindowState win) {
+ final int behavior = win.mAttrs.insetsFlags.behavior;
+ return mNavigationBar != null
+ && canHideNavigationBar()
+ && (behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE
+ || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
+ && getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)
+ && win != getNotificationShade();
+ }
+
/**
* @return whether the navigation bar can be hidden, e.g. the device has a navigation bar
*/
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 64c5faa..57babb0 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -558,16 +558,14 @@
@VisibleForTesting
boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
- final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
- if (w == null) {
- return false;
- }
// Display doesn't need to be frozen because application has been started in correct
// rotation already, so the rest of the windows can use seamless rotation.
- if (w.mToken.hasFixedRotationTransform()) {
+ if (mDisplayContent.mFixedRotationLaunchingApp != null) {
return true;
}
- if (w != mDisplayContent.mCurrentFocus) {
+
+ final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
+ if (w == null || w != mDisplayContent.mCurrentFocus) {
return false;
}
// We only enable seamless rotation if the top window has requested it and is in the
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 9d985d7..c92de2b 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -76,7 +76,7 @@
// to be visible (such as performing Recents animation).
final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
&& mContiner.isTopActivityFocusable()
- && mContiner.isInStackLocked(starting) == null;
+ && (starting == null || !starting.isDescendantOf(mContiner));
final PooledConsumer f = PooledLambda.obtainConsumer(
EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a8fe349..b3890cd 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -182,6 +182,7 @@
}
if (changed) {
notifyInsetsChanged();
+ mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index b0492be..e92bbaa 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -334,7 +334,7 @@
if (sendUserLeaveHint) {
// Setting this allows the previous app to PiP.
mStackSupervisor.mUserLeaving = true;
- targetStack.moveTaskToFrontLocked(targetActivity.getTask(),
+ targetStack.moveTaskToFront(targetActivity.getTask(),
true /* noAnimation */, null /* activityOptions */,
targetActivity.appTimeTracker,
"RecentsAnimation.onAnimationFinished()");
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e923e64..57c877f 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -376,7 +376,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
{ if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
visibleTasks);
- targetStack.forAllTasks(c, true /* traverseTopToBottom */, targetStack);
+ targetStack.forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
}
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 63346b9..45f8a15 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -67,7 +67,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(
ResetTargetTaskHelper::processTask, this, PooledLambda.__(Task.class));
- targetTask.mWmService.mRoot.forAllTasks(c, true /*traverseTopToBottom*/, mTargetStack);
+ targetTask.mWmService.mRoot.forAllLeafTasks(c, true /*traverseTopToBottom*/);
c.recycle();
processPendingReparentActivities();
@@ -245,9 +245,8 @@
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+ r + " out to bottom task " + targetTask);
} else {
- targetTask = mTargetStack.createTask(
- atmService.mStackSupervisor.getNextTaskIdForUser(r.mUserId), r.info,
- null /* intent */, false /* toTop */);
+ targetTask = mTargetStack.reuseOrCreateTask(
+ r.info, null /*intent*/, false /*toTop*/);
targetTask.affinityIntent = r.intent;
createdTasks.add(targetTask);
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2596452..aa6bdfd 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2137,10 +2137,7 @@
r.getActivityType(), ON_TOP, r.info, r.intent);
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
-
- Task newTask = stack.createTask(mStackSupervisor.getNextTaskIdForUser(r.mUserId),
- r.info, r.intent, true);
- r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+ r.reparent(stack, MAX_VALUE, "moveActivityToStack");
}
stack.setWindowingMode(WINDOWING_MODE_PINNED);
@@ -2407,7 +2404,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(
RootWindowContainer::processTaskForStackInfo, PooledLambda.__(Task.class), info,
currentIndex);
- stack.forAllTasks(c, false /* traverseTopToBottom */, stack);
+ stack.forAllLeafTasks(c, false /* traverseTopToBottom */);
c.recycle();
final ActivityRecord top = stack.topRunningActivity();
@@ -3302,7 +3299,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(
RootWindowContainer::taskTopActivityIsUser, this, PooledLambda.__(Task.class),
userId);
- forAllTasks(c);
+ forAllLeafTasks(c, true /* traverseTopToBottom */);
c.recycle();
} finally {
mService.continueWindowLayout();
@@ -3321,14 +3318,6 @@
* @return {@code true} if the top activity looks like it belongs to {@param userId}.
*/
private void taskTopActivityIsUser(Task task, @UserIdInt int userId) {
- // TODO(b/80414790): having utilities to loop for all leaf tasks from caller vs. checking
- // leaf tasks here.
- if (!task.isLeafTask()) {
- // No op if not a leaf task since we don't want to report root tasks to
- // TaskStackListeners.
- return;
- }
-
// To handle the case that work app is in the task but just is not the top one.
final ActivityRecord activityRecord = task.getTopNonFinishingActivity();
final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
@@ -3430,18 +3419,8 @@
}
ActivityRecord isInAnyStack(IBinder token) {
- int numDisplays = getChildCount();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getStackAt(stackNdx);
- final ActivityRecord r = stack.isInStackLocked(token);
- if (r != null) {
- return r;
- }
- }
- }
- return null;
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ return (r != null && r.isDescendantOf(this)) ? r : null;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 6ebbf77..9593ea0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -76,7 +76,7 @@
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
PooledLambda.__(Task.class));
- root.forAllTasks(c, false);
+ root.forAllLeafTasks(c, false);
c.recycle();
// Take the first {@param maxNum} tasks and create running task infos for them
@@ -93,9 +93,6 @@
}
private void processTask(Task task) {
- if (task.isRootTask()) {
- return;
- }
if (task.getTopNonFinishingActivity() == null) {
// Skip if there are no activities in the task
return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c7f2cc7..f93e392 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -225,8 +225,8 @@
String affinity; // The affinity name for this task, or null; may change identity.
String rootAffinity; // Initial base affinity, or null; does not change from initial root.
- final IVoiceInteractionSession voiceSession; // Voice interaction session driving task
- final IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
+ IVoiceInteractionSession voiceSession; // Voice interaction session driving task
+ IVoiceInteractor voiceInteractor; // Associated interactor to provide to app
Intent intent; // The original intent that started the task. Note that this value can
// be null.
Intent affinityIntent; // Intent of affinity-moved activity that started this task.
@@ -422,6 +422,8 @@
/** When set, will force the task to report as invisible. */
boolean mForceHidden = false;
+ SurfaceControl.Transaction mMainWindowSizeChangeTransaction;
+
private final FindRootHelper mFindRootHelper = new FindRootHelper();
private class FindRootHelper {
private ActivityRecord mRoot;
@@ -571,6 +573,15 @@
mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
+ Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
+ ActivityInfo info, ActivityRecord activity) {
+ voiceSession = _voiceSession;
+ voiceInteractor = _voiceInteractor;
+ setIntent(activity);
+ setMinDimensions(info);
+ return this;
+ }
+
private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
if (hasChild()) {
return;
@@ -1004,7 +1015,7 @@
}
/** Sets the original minimal width and height. */
- private void setMinDimensions(ActivityInfo info) {
+ void setMinDimensions(ActivityInfo info) {
if (info != null && info.windowLayout != null) {
mMinWidth = info.windowLayout.minWidth;
mMinHeight = info.windowLayout.minHeight;
@@ -2176,16 +2187,20 @@
return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
}
+ void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+ super.resolveOverrideConfiguration(newParentConfig);
+ }
+
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
- if (isRootTask()) {
- super.resolveOverrideConfiguration(newParentConfig);
+ if (!isLeafTask()) {
+ resolveTileOverrideConfiguration(newParentConfig);
return;
}
mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
- super.resolveOverrideConfiguration(newParentConfig);
+ resolveTileOverrideConfiguration(newParentConfig);
int windowingMode =
- getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
}
@@ -2392,7 +2407,7 @@
final int[] currentCount = {0};
final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
PooledLambda.__(Task.class), currentCount);
- forAllTasks(c, false /* traverseTopToBottom */, this);
+ forAllLeafTasks(c, false /* traverseTopToBottom */);
c.recycle();
return currentCount[0];
}
@@ -2614,6 +2629,7 @@
*/
void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
if (overrideDisplayedBounds != null) {
+ adjustForMinimalTaskDimensions(overrideDisplayedBounds, mOverrideDisplayedBounds);
mOverrideDisplayedBounds.set(overrideDisplayedBounds);
} else {
mOverrideDisplayedBounds.setEmpty();
@@ -3074,16 +3090,33 @@
}
@Override
- void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom, Task excludedTask) {
- super.forAllTasks(callback, traverseTopToBottom, excludedTask);
- if (excludedTask != this) {
- callback.accept(this);
+ void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
+ final int count = mChildren.size();
+ boolean isLeafTask = true;
+ if (traverseTopToBottom) {
+ for (int i = count - 1; i >= 0; --i) {
+ final Task child = mChildren.get(i).asTask();
+ if (child != null) {
+ isLeafTask = false;
+ child.forAllLeafTasks(callback, traverseTopToBottom);
+ }
+ }
+ } else {
+ for (int i = 0; i < count; i++) {
+ final Task child = mChildren.get(i).asTask();
+ if (child != null) {
+ isLeafTask = false;
+ child.forAllLeafTasks(callback, traverseTopToBottom);
+ }
+ }
}
+ if (isLeafTask) callback.accept(this);
}
@Override
void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
- forAllTasks(callback, traverseTopToBottom, null /* excludedTask */);
+ super.forAllTasks(callback, traverseTopToBottom);
+ callback.accept(this);
}
@Override
@@ -3265,7 +3298,7 @@
pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid);
pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
- pw.print(" mCallingPackage="); pw.println(mCallingPackage);
+ pw.print(" mCallingPackage="); pw.print(mCallingPackage);
pw.print(" mCallingFeatureId="); pw.println(mCallingFeatureId);
if (affinity != null || rootAffinity != null) {
pw.print(prefix); pw.print("affinity="); pw.print(affinity);
@@ -3979,4 +4012,17 @@
mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
this, true /* force */);
}
+
+ /**
+ * See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
+ * transaction will be consumed by the next BASE_APPLICATION window within our hierarchy
+ * to resize, and it will defer the transaction until that resize frame completes.
+ */
+ void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t) {
+ mMainWindowSizeChangeTransaction = t;
+ }
+
+ SurfaceControl.Transaction getMainWindowSizeChangeTransaction() {
+ return mMainWindowSizeChangeTransaction;
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4d5621c..6caa27c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -547,8 +547,7 @@
final ActivityStack stack = (ActivityStack) container;
if (stack.inPinnedWindowingMode()) {
stack.resize(config.windowConfiguration.getBounds(),
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
+ null /* configBounds */, PRESERVE_WINDOWS, true /* deferResume */);
}
}
}
@@ -557,6 +556,12 @@
WindowContainerTransaction.Change c) {
int effects = sanitizeAndApplyChange(wc, c);
+ final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
+ if (t != null) {
+ Task tr = (Task) wc;
+ tr.setMainWindowSizeChangeTransaction(t);
+ }
+
Rect enterPipBounds = c.getEnterPipBounds();
if (enterPipBounds != null) {
Task tr = (Task) wc;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index da996dc..7a4d0b0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -615,7 +615,7 @@
void positionChildAt(int position, E child, boolean includingParents) {
if (child.getParent() != this) {
- throw new IllegalArgumentException("removeChild: container=" + child.getName()
+ throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
+ " is not a child of container=" + getName()
+ " current parent=" + child.getParent());
}
@@ -1459,15 +1459,15 @@
}
}
- void forAllTasks(Consumer<Task> callback, boolean traverseTopToBottom, Task excludedTask) {
+ void forAllLeafTasks(Consumer<Task> callback, boolean traverseTopToBottom) {
final int count = mChildren.size();
if (traverseTopToBottom) {
for (int i = count - 1; i >= 0; --i) {
- mChildren.get(i).forAllTasks(callback, traverseTopToBottom, excludedTask);
+ mChildren.get(i).forAllLeafTasks(callback, traverseTopToBottom);
}
} else {
for (int i = 0; i < count; i++) {
- mChildren.get(i).forAllTasks(callback, traverseTopToBottom, excludedTask);
+ mChildren.get(i).forAllLeafTasks(callback, traverseTopToBottom);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9552df7..a1a9af6 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -873,6 +873,14 @@
clipRect = mTmpClipRect;
}
+ if (mSurfaceResized && (mAttrType == TYPE_BASE_APPLICATION) &&
+ (task != null) && (task.getMainWindowSizeChangeTransaction() != null)) {
+ mSurfaceController.deferTransactionUntil(mWin.getDeferTransactionBarrier(),
+ mWin.getFrameNumber());
+ SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction());
+ task.setMainWindowSizeChangeTransaction(null);
+ }
+
float surfaceWidth = mSurfaceController.getWidth();
float surfaceHeight = mSurfaceController.getHeight();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 36ed9a5..336934e 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -166,7 +166,10 @@
using MeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using MeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
-using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using SingleSatCorrection_V1_0 =
+ android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection;
+using SingleSatCorrection_V1_1 =
+ android::hardware::gnss::measurement_corrections::V1_1::SingleSatCorrection;
using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane;
using android::hidl::base::V1_0::IBase;
@@ -3124,6 +3127,91 @@
return JNI_FALSE;
}
+static SingleSatCorrection_V1_0 getSingleSatCorrection_1_0_withoutConstellation(
+ JNIEnv* env, jobject singleSatCorrectionObj) {
+ jint correctionFlags = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags);
+ jint satId = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
+ jfloat carrierFreqHz =
+ env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatCarrierFreq);
+ jfloat probSatIsLos =
+ env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatIsLosProb);
+ jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
+ jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc);
+ uint16_t corrFlags = static_cast<uint16_t>(correctionFlags);
+ jobject reflectingPlaneObj;
+ bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0;
+ if (has_ref_plane) {
+ reflectingPlaneObj =
+ env->CallObjectMethod(singleSatCorrectionObj, method_correctionSatRefPlane);
+ }
+
+ ReflectingPlane reflectingPlane;
+ if (has_ref_plane) {
+ jdouble latitudeDegreesRefPlane =
+ env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneLatDeg);
+ jdouble longitudeDegreesRefPlane =
+ env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneLngDeg);
+ jdouble altitudeDegreesRefPlane =
+ env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneAltDeg);
+ jdouble azimuthDegreeRefPlane =
+ env->CallDoubleMethod(reflectingPlaneObj, method_correctionPlaneAzimDeg);
+ reflectingPlane = {
+ .latitudeDegrees = latitudeDegreesRefPlane,
+ .longitudeDegrees = longitudeDegreesRefPlane,
+ .altitudeMeters = altitudeDegreesRefPlane,
+ .azimuthDegrees = azimuthDegreeRefPlane,
+ };
+ }
+
+ SingleSatCorrection_V1_0 singleSatCorrection = {
+ .singleSatCorrectionFlags = corrFlags,
+ .svid = static_cast<uint16_t>(satId),
+ .carrierFrequencyHz = carrierFreqHz,
+ .probSatIsLos = probSatIsLos,
+ .excessPathLengthMeters = eplMeters,
+ .excessPathLengthUncertaintyMeters = eplUncMeters,
+ .reflectingPlane = reflectingPlane,
+ };
+
+ return singleSatCorrection;
+}
+
+static void getSingleSatCorrectionList_1_1(JNIEnv* env, jobject singleSatCorrectionList,
+ hidl_vec<SingleSatCorrection_V1_1>& list) {
+ for (uint16_t i = 0; i < list.size(); ++i) {
+ jobject singleSatCorrectionObj =
+ env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i);
+
+ SingleSatCorrection_V1_0 singleSatCorrection_1_0 =
+ getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj);
+
+ jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType);
+
+ SingleSatCorrection_V1_1 singleSatCorrection_1_1 = {
+ .v1_0 = singleSatCorrection_1_0,
+ .constellation = static_cast<GnssConstellationType_V2_0>(constType),
+ };
+
+ list[i] = singleSatCorrection_1_1;
+ }
+}
+
+static void getSingleSatCorrectionList_1_0(JNIEnv* env, jobject singleSatCorrectionList,
+ hidl_vec<SingleSatCorrection_V1_0>& list) {
+ for (uint16_t i = 0; i < list.size(); ++i) {
+ jobject singleSatCorrectionObj =
+ env->CallObjectMethod(singleSatCorrectionList, method_correctionListGet, i);
+
+ SingleSatCorrection_V1_0 singleSatCorrection =
+ getSingleSatCorrection_1_0_withoutConstellation(env, singleSatCorrectionObj);
+
+ jint constType = env->CallIntMethod(singleSatCorrectionObj, method_correctionSatConstType);
+
+ singleSatCorrection.constellation = static_cast<GnssConstellationType_V1_0>(constType),
+
+ list[i] = singleSatCorrection;
+ }
+}
static jboolean
android_location_GnssMeasurementCorrectionsProvider_inject_gnss_measurement_corrections(
JNIEnv* env,
@@ -3146,64 +3234,6 @@
ALOGI("Empty correction list injected....Returning with no HAL injection");
return JNI_TRUE;
}
- hidl_vec<SingleSatCorrection> list(len);
-
- for (uint16_t i = 0; i < len; ++i) {
- jobject singleSatCorrectionObj = env->CallObjectMethod(
- singleSatCorrectionList, method_correctionListGet, i);
-
- jint correctionFlags =
- env->CallIntMethod(singleSatCorrectionObj, method_correctionSatFlags);
- jint constType = env->CallIntMethod(singleSatCorrectionObj,
- method_correctionSatConstType);
- jint satId =
- env->CallIntMethod(singleSatCorrectionObj, method_correctionSatId);
- jfloat carrierFreqHz = env->CallFloatMethod(
- singleSatCorrectionObj, method_correctionSatCarrierFreq);
- jfloat probSatIsLos = env->CallFloatMethod(singleSatCorrectionObj,
- method_correctionSatIsLosProb);
- jfloat eplMeters =
- env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
- jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj,
- method_correctionSatEplUnc);
- uint16_t corrFlags = static_cast<uint16_t>(correctionFlags);
- jobject reflectingPlaneObj;
- bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0;
- if (has_ref_plane) {
- reflectingPlaneObj = env->CallObjectMethod(
- singleSatCorrectionObj, method_correctionSatRefPlane);
- }
-
- ReflectingPlane reflectingPlane;
- if (has_ref_plane) {
- jdouble latitudeDegreesRefPlane = env->CallDoubleMethod(
- reflectingPlaneObj, method_correctionPlaneLatDeg);
- jdouble longitudeDegreesRefPlane = env->CallDoubleMethod(
- reflectingPlaneObj, method_correctionPlaneLngDeg);
- jdouble altitudeDegreesRefPlane = env->CallDoubleMethod(
- reflectingPlaneObj, method_correctionPlaneAltDeg);
- jdouble azimuthDegreeRefPlane = env->CallDoubleMethod(
- reflectingPlaneObj, method_correctionPlaneAzimDeg);
- reflectingPlane = {
- .latitudeDegrees = latitudeDegreesRefPlane,
- .longitudeDegrees = longitudeDegreesRefPlane,
- .altitudeMeters = altitudeDegreesRefPlane,
- .azimuthDegrees = azimuthDegreeRefPlane,
- };
- }
-
- SingleSatCorrection singleSatCorrection = {
- .singleSatCorrectionFlags = corrFlags,
- .constellation = static_cast<GnssConstellationType_V1_0>(constType),
- .svid = static_cast<uint16_t>(satId),
- .carrierFrequencyHz = carrierFreqHz,
- .probSatIsLos = probSatIsLos,
- .excessPathLengthMeters = eplMeters,
- .excessPathLengthUncertaintyMeters = eplUncMeters,
- .reflectingPlane = reflectingPlane,
- };
- list[i] = singleSatCorrection;
- }
jdouble latitudeDegreesCorr = env->CallDoubleMethod(
correctionsObj, method_correctionsGetLatitudeDegrees);
@@ -3225,7 +3255,6 @@
.horizontalPositionUncertaintyMeters = horizontalPositionUncertaintyMeters,
.verticalPositionUncertaintyMeters = verticalPositionUncertaintyMeters,
.toaGpsNanosecondsOfWeek = static_cast<uint64_t>(toaGpsNanosOfWeek),
- .satCorrections = list,
};
if (gnssCorrectionsIface_V1_1 != nullptr) {
@@ -3237,17 +3266,25 @@
jfloat environmentBearingUncertaintyDegreesCorr = env->CallFloatMethod(
correctionsObj, method_correctionsGetEnvironmentBearingUncertaintyDegrees);
+ hidl_vec<SingleSatCorrection_V1_1> list(len);
+ getSingleSatCorrectionList_1_1(env, singleSatCorrectionList, list);
+
MeasurementCorrections_V1_1 measurementCorrections_1_1 = {
- .v1_0 = measurementCorrections_1_0,
- .hasEnvironmentBearing = static_cast<bool>(hasEnvironmentBearingCorr),
- .environmentBearingDegrees = environmentBearingDegreesCorr,
- .environmentBearingUncertaintyDegrees = environmentBearingUncertaintyDegreesCorr,
+ .v1_0 = measurementCorrections_1_0,
+ .hasEnvironmentBearing = static_cast<bool>(hasEnvironmentBearingCorr),
+ .environmentBearingDegrees = environmentBearingDegreesCorr,
+ .environmentBearingUncertaintyDegrees = environmentBearingUncertaintyDegreesCorr,
+ .satCorrections = list,
};
auto result = gnssCorrectionsIface_V1_1->setCorrections_1_1(measurementCorrections_1_1);
return checkHidlReturn(result, "IMeasurementCorrections 1.1 setCorrections() failed.");
}
+ hidl_vec<SingleSatCorrection_V1_0> list(len);
+ getSingleSatCorrectionList_1_0(env, singleSatCorrectionList, list);
+ measurementCorrections_1_0.satCorrections = list;
+
auto result = gnssCorrectionsIface_V1_0->setCorrections(measurementCorrections_1_0);
return checkHidlReturn(result, "IMeasurementCorrections 1.0 setCorrections() failed.");
}
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 70a9c09..f445aa8 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -190,6 +190,7 @@
}
static inline int32_t skipIdSigHeaders(borrowed_fd fd) {
+ readBEInt32(fd); // version
readBytes(fd); // verityRootHash
readBytes(fd); // v3Digest
readBytes(fd); // pkcs7SignatureBlock
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b239b684..46d8800 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -180,14 +180,12 @@
import android.content.pm.ServiceInfo;
import android.content.pm.StringParceledListSlice;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.AndroidPackage;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.location.LocationManager;
-import android.location.LocationManagerInternal;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
@@ -290,6 +288,7 @@
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -445,10 +444,13 @@
});
/**
- * System property whose value is either "true" or "false", indicating whether
- * device owner is present.
+ * System property whose value indicates whether the device is fully owned by an organization:
+ * it can be either a device owner device, or a device with an organization-owned managed
+ * profile.
+ *
+ * <p>The state is stored as a Boolean string.
*/
- private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.device_owner";
+ private static final String PROPERTY_ORGANIZATION_OWNED = "ro.organization_owned";
private static final int STATUS_BAR_DISABLE_MASK =
StatusBarManager.DISABLE_EXPAND |
@@ -481,6 +483,7 @@
GLOBAL_SETTINGS_WHITELIST = new ArraySet<>();
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED);
+ GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_WIFI_ENABLED);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING);
@@ -1073,7 +1076,8 @@
private static final String TAG_SUSPEND_PERSONAL_APPS = "suspend-personal-apps";
private static final String TAG_PROFILE_MAXIMUM_TIME_OFF = "profile-max-time-off";
private static final String TAG_PROFILE_OFF_DEADLINE = "profile-off-deadline";
-
+ private static final String TAG_ALWAYS_ON_VPN_PACKAGE = "vpn-package";
+ private static final String TAG_ALWAYS_ON_VPN_LOCKDOWN = "vpn-lockdown";
DeviceAdminInfo info;
@@ -1202,6 +1206,9 @@
// Time by which the profile should be turned on according to System.currentTimeMillis().
long mProfileOffDeadline = 0;
+ public String mAlwaysOnVpnPackage;
+ public boolean mAlwaysOnVpnLockdown;
+
ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
info = _info;
@@ -1442,6 +1449,12 @@
if (mProfileMaximumTimeOff != 0) {
writeAttributeValueToXml(out, TAG_PROFILE_OFF_DEADLINE, mProfileOffDeadline);
}
+ if (!TextUtils.isEmpty(mAlwaysOnVpnPackage)) {
+ writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_PACKAGE, mAlwaysOnVpnPackage);
+ }
+ if (mAlwaysOnVpnLockdown) {
+ writeAttributeValueToXml(out, TAG_ALWAYS_ON_VPN_LOCKDOWN, mAlwaysOnVpnLockdown);
+ }
}
void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
@@ -1687,6 +1700,11 @@
} else if (TAG_PROFILE_OFF_DEADLINE.equals(tag)) {
mProfileOffDeadline =
Long.parseLong(parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_ALWAYS_ON_VPN_PACKAGE.equals(tag)) {
+ mAlwaysOnVpnPackage = parser.getAttributeValue(null, ATTR_VALUE);
+ } else if (TAG_ALWAYS_ON_VPN_LOCKDOWN.equals(tag)) {
+ mAlwaysOnVpnLockdown = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1919,6 +1937,10 @@
pw.println(mProfileMaximumTimeOff);
pw.print("mProfileOffDeadline=");
pw.println(mProfileOffDeadline);
+ pw.print("mAlwaysOnVpnPackage=");
+ pw.println(mAlwaysOnVpnPackage);
+ pw.print("mAlwaysOnVpnLockdown=");
+ pw.println(mAlwaysOnVpnLockdown);
}
}
@@ -2112,10 +2134,6 @@
return mContext.getSystemService(LocationManager.class);
}
- LocationManagerInternal getLocationManagerInternal() {
- return LocalServices.getService(LocationManagerInternal.class);
- }
-
IWindowManager getIWindowManager() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
@@ -2536,7 +2554,7 @@
void loadOwners() {
synchronized (getLockObject()) {
mOwners.load();
- setDeviceOwnerSystemPropertyLocked();
+ setDeviceOwnershipSystemPropertyLocked();
findOwnerComponentIfNecessaryLocked();
migrateUserRestrictionsIfNecessaryLocked();
@@ -2738,34 +2756,36 @@
}
}
- private void setDeviceOwnerSystemPropertyLocked() {
- final boolean deviceProvisioned =
- mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
- final boolean hasDeviceOwner = mOwners.hasDeviceOwner();
- // If the device is not provisioned and there is currently no device owner, do not set the
- // read-only system property yet, since Device owner may still be provisioned.
- if (!hasDeviceOwner && !deviceProvisioned) {
- return;
- }
- // Still at the first stage of CryptKeeper double bounce, mOwners.hasDeviceOwner is
- // always false at this point.
+ private void setDeviceOwnershipSystemPropertyLocked() {
+ // Still at the first stage of CryptKeeper double bounce, nothing can be learnt about
+ // the real system at this point.
if (StorageManager.inCryptKeeperBounce()) {
return;
}
-
- if (!mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT, "").isEmpty()) {
- Slog.w(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
- } else {
- final String value = Boolean.toString(hasDeviceOwner);
- mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, value);
- Slog.i(LOG_TAG, "Set ro.device_owner property to " + value);
+ final boolean deviceProvisioned =
+ mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ final boolean hasDeviceOwner = mOwners.hasDeviceOwner();
+ final boolean hasOrgOwnedProfile = isOrganizationOwnedDeviceWithManagedProfile();
+ // If the device is not provisioned and there is currently no management, do not set the
+ // read-only system property yet, since device owner / org-owned profile may still be
+ // provisioned.
+ if (!hasDeviceOwner && !hasOrgOwnedProfile && !deviceProvisioned) {
+ return;
+ }
+ final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile);
+ final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null);
+ if (TextUtils.isEmpty(currentVal)) {
+ Slog.i(LOG_TAG, "Set ro.organization_owned property to " + value);
+ mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value);
+ } else if (!value.equals(currentVal)) {
+ Slog.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
}
}
private void maybeStartSecurityLogMonitorOnActivityManagerReady() {
synchronized (getLockObject()) {
if (mInjector.securityLogIsLoggingEnabled()) {
- mSecurityLogMonitor.start();
+ mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
mInjector.runCryptoSelfTest();
maybePauseDeviceWideLoggingLocked();
}
@@ -6781,10 +6801,10 @@
* @throws UnsupportedOperationException if the package does not support being set as always-on.
*/
@Override
- public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage, boolean lockdown,
+ public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown,
List<String> lockdownWhitelist)
throws SecurityException {
- enforceProfileOrDeviceOwner(admin);
+ enforceProfileOrDeviceOwner(who);
final int userId = mInjector.userHandleGetCallingUserId();
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -6810,12 +6830,22 @@
}
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_ALWAYS_ON_VPN_PACKAGE)
- .setAdmin(admin)
+ .setAdmin(who)
.setStrings(vpnPackage)
.setBoolean(lockdown)
.setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0)
.write();
});
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (!TextUtils.equals(vpnPackage, admin.mAlwaysOnVpnPackage)
+ || lockdown != admin.mAlwaysOnVpnLockdown) {
+ admin.mAlwaysOnVpnPackage = vpnPackage;
+ admin.mAlwaysOnVpnLockdown = lockdown;
+ saveSettingsLocked(userId);
+ }
+ }
return true;
}
@@ -6829,6 +6859,15 @@
}
@Override
+ public String getAlwaysOnVpnPackageForUser(int userHandle) {
+ enforceSystemCaller("getAlwaysOnVpnPackageForUser");
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
+ return admin != null ? admin.mAlwaysOnVpnPackage : null;
+ }
+ }
+
+ @Override
public boolean isAlwaysOnVpnLockdownEnabled(ComponentName admin) throws SecurityException {
enforceProfileOrDeviceOwner(admin);
@@ -6838,6 +6877,15 @@
}
@Override
+ public boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle) {
+ enforceSystemCaller("isAlwaysOnVpnLockdownEnabledForUser");
+ synchronized (getLockObject()) {
+ ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
+ return admin != null ? admin.mAlwaysOnVpnLockdown : null;
+ }
+ }
+
+ @Override
public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
throws SecurityException {
enforceProfileOrDeviceOwner(admin);
@@ -8349,7 +8397,7 @@
mOwners.setDeviceOwner(admin, ownerName, userId);
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
- setDeviceOwnerSystemPropertyLocked();
+ setDeviceOwnershipSystemPropertyLocked();
mInjector.binderWithCleanCallingIdentity(() -> {
// Restrict adding a managed profile when a device owner is set on the device.
@@ -8987,6 +9035,19 @@
return null;
}
+ /**
+ * Returns the ActiveAdmin associated wit the PO or DO on the given user.
+ * @param userHandle
+ * @return
+ */
+ private @Nullable ActiveAdmin getDeviceOrProfileOwnerAdminLocked(int userHandle) {
+ ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+ if (admin == null && getDeviceOwnerUserId() == userHandle) {
+ admin = getDeviceOwnerAdminLocked();
+ }
+ return admin;
+ }
+
@GuardedBy("getLockObject()")
ActiveAdmin getProfileOwnerOfOrganizationOwnedDeviceLocked(int userHandle) {
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -9016,21 +9077,21 @@
return getApplicationLabel(profileOwner.getPackageName(), userHandle);
}
+ private @UserIdInt int getOrganizationOwnedProfileUserId() {
+ for (UserInfo ui : mUserManagerInternal.getUserInfos()) {
+ if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) {
+ return ui.id;
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
@Override
public boolean isOrganizationOwnedDeviceWithManagedProfile() {
if (!mHasFeature) {
return false;
}
-
- return mInjector.binderWithCleanCallingIdentity(() -> {
- for (UserInfo ui : mUserManager.getUsers()) {
- if (ui.isManagedProfile() && isProfileOwnerOfOrganizationOwnedDevice(ui.id)) {
- return true;
- }
- }
-
- return false;
- });
+ return getOrganizationOwnedProfileUserId() != UserHandle.USER_NULL;
}
@Override
@@ -11643,17 +11704,6 @@
}
@Override
- public void requestSetLocationProviderAllowed(ComponentName who, String provider,
- boolean providerAllowed) {
- Objects.requireNonNull(who, "ComponentName is null");
- enforceDeviceOwner(who);
-
- mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getLocationManagerInternal().requestSetProviderAllowed(provider,
- providerAllowed));
- }
-
- @Override
public boolean setTime(ComponentName who, long millis) {
Objects.requireNonNull(who, "ComponentName is null in setTime");
enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
@@ -12002,7 +12052,7 @@
synchronized (getLockObject()) {
// Set PROPERTY_DEVICE_OWNER_PRESENT, for the SUW case where setting the property
// is delayed until device is marked as provisioned.
- setDeviceOwnerSystemPropertyLocked();
+ setDeviceOwnershipSystemPropertyLocked();
}
} else if (mDefaultImeChanged.equals(uri)) {
synchronized (getLockObject()) {
@@ -13627,6 +13677,22 @@
});
}
+ private boolean canStartSecurityLogging() {
+ synchronized (getLockObject()) {
+ return isOrganizationOwnedDeviceWithManagedProfile()
+ || areAllUsersAffiliatedWithDeviceLocked();
+ }
+ }
+
+ private @UserIdInt int getSecurityLoggingEnabledUser() {
+ synchronized (getLockObject()) {
+ if (mOwners.hasDeviceOwner()) {
+ return UserHandle.USER_ALL;
+ }
+ }
+ return getOrganizationOwnedProfileUserId();
+ }
+
@Override
public void setSecurityLoggingEnabled(ComponentName admin, boolean enabled) {
if (!mHasFeature) {
@@ -13635,13 +13701,14 @@
Objects.requireNonNull(admin);
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) {
return;
}
mInjector.securityLogSetLoggingEnabledProperty(enabled);
if (enabled) {
- mSecurityLogMonitor.start();
+ mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
maybePauseDeviceWideLoggingLocked();
} else {
mSecurityLogMonitor.stop();
@@ -13663,7 +13730,8 @@
synchronized (getLockObject()) {
if (!isCallerWithSystemUid()) {
Objects.requireNonNull(admin);
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
}
return mInjector.securityLogGetLoggingEnabledProperty();
}
@@ -13687,7 +13755,10 @@
}
Objects.requireNonNull(admin);
- ensureDeviceOwnerAndAllUsersAffiliated(admin);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin);
+ if (!isOrganizationOwnedDeviceWithManagedProfile()) {
+ ensureAllUsersAffiliated();
+ }
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RETRIEVE_PRE_REBOOT_SECURITY_LOGS)
@@ -13703,6 +13774,10 @@
ArrayList<SecurityEvent> output = new ArrayList<SecurityEvent>();
try {
SecurityLog.readPreviousEvents(output);
+ int enabledUser = getSecurityLoggingEnabledUser();
+ if (enabledUser != UserHandle.USER_ALL) {
+ SecurityLog.redactEvents(output, enabledUser);
+ }
return new ParceledListSlice<SecurityEvent>(output);
} catch (IOException e) {
Slog.w(LOG_TAG, "Fail to read previous events" , e);
@@ -13717,7 +13792,10 @@
}
Objects.requireNonNull(admin);
- ensureDeviceOwnerAndAllUsersAffiliated(admin);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin);
+ if (!isOrganizationOwnedDeviceWithManagedProfile()) {
+ ensureAllUsersAffiliated();
+ }
if (!mInjector.securityLogGetLoggingEnabledProperty()) {
return null;
@@ -14243,26 +14321,34 @@
@GuardedBy("getLockObject()")
private void maybePauseDeviceWideLoggingLocked() {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
- Slog.i(LOG_TAG, "There are unaffiliated users, security and network logging will be "
+ Slog.i(LOG_TAG, "There are unaffiliated users, network logging will be "
+ "paused if enabled.");
- mSecurityLogMonitor.pause();
if (mNetworkLogger != null) {
mNetworkLogger.pause();
}
+ if (!isOrganizationOwnedDeviceWithManagedProfile()) {
+ Slog.i(LOG_TAG, "Not org-owned managed profile device, security logging will be "
+ + "paused if enabled.");
+ mSecurityLogMonitor.pause();
+ }
}
}
/** Resumes security and network logging (if they are enabled) if all users are affiliated */
@GuardedBy("getLockObject()")
private void maybeResumeDeviceWideLoggingLocked() {
- if (areAllUsersAffiliatedWithDeviceLocked()) {
- mInjector.binderWithCleanCallingIdentity(() -> {
+ boolean allUsersAffiliated = areAllUsersAffiliatedWithDeviceLocked();
+ boolean orgOwnedProfileDevice = isOrganizationOwnedDeviceWithManagedProfile();
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (allUsersAffiliated || orgOwnedProfileDevice) {
mSecurityLogMonitor.resume();
+ }
+ if (allUsersAffiliated) {
if (mNetworkLogger != null) {
mNetworkLogger.resume();
}
- });
- }
+ }
+ });
}
/** Deletes any security and network logs that might have been collected so far */
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 1ab3b98..3c445ca 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -51,15 +51,17 @@
private final Lock mLock = new ReentrantLock();
+ private int mEnabledUser;
+
SecurityLogMonitor(DevicePolicyManagerService service) {
this(service, 0 /* id */);
}
@VisibleForTesting
SecurityLogMonitor(DevicePolicyManagerService service, long id) {
- this.mService = service;
- this.mId = id;
- this.mLastForceNanos = System.nanoTime();
+ mService = service;
+ mId = id;
+ mLastForceNanos = System.nanoTime();
}
private static final boolean DEBUG = false; // STOPSHIP if true.
@@ -136,8 +138,15 @@
@GuardedBy("mForceSemaphore")
private long mLastForceNanos = 0;
- void start() {
- Slog.i(TAG, "Starting security logging.");
+ /**
+ * Start security logging.
+ *
+ * @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all
+ * users on the device.
+ */
+ void start(int enabledUser) {
+ Slog.i(TAG, "Starting security logging for user " + enabledUser);
+ mEnabledUser = enabledUser;
SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED);
mLock.lock();
try {
@@ -286,7 +295,7 @@
break;
}
}
-
+ SecurityLog.redactEvents(newLogs, mEnabledUser);
if (DEBUG) Slog.d(TAG, "Got " + newLogs.size() + " new events.");
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 569986c..b5d3d18 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -44,7 +44,6 @@
import android.graphics.GraphicsStatsService;
import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityModuleConnector;
-import android.net.ITetheringConnector;
import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
@@ -298,6 +297,9 @@
"com.android.server.blob.BlobStoreManagerService";
private static final String APP_SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.appsearch.AppSearchManagerService";
+
+ private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
+
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
@@ -2331,7 +2333,7 @@
try {
// TODO: hide implementation details, b/146312721.
ConnectivityModuleConnector.getInstance().startModuleService(
- ITetheringConnector.class.getName(),
+ TETHERING_CONNECTOR_CLASS,
PERMISSION_MAINLINE_NETWORK_STACK, service -> {
ServiceManager.addService(Context.TETHERING_SERVICE, service,
false /* allowIsolated */,
diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
index 203e980..7672cd0 100644
--- a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
+++ b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
@@ -51,15 +51,18 @@
abstract class AbstractProtoDiskReadWriter<T> {
private static final String TAG = AbstractProtoDiskReadWriter.class.getSimpleName();
+
+ // Common disk write delay that will be appropriate for most scenarios.
+ private static final long DEFAULT_DISK_WRITE_DELAY = 2L * DateUtils.MINUTE_IN_MILLIS;
private static final long SHUTDOWN_DISK_WRITE_TIMEOUT = 5L * DateUtils.SECOND_IN_MILLIS;
private final File mRootDir;
private final ScheduledExecutorService mScheduledExecutorService;
- private final long mWriteDelayMs;
@GuardedBy("this")
private ScheduledFuture<?> mScheduledFuture;
+ // File name -> data class
@GuardedBy("this")
private Map<String, T> mScheduledFileDataMap = new ArrayMap<>();
@@ -75,15 +78,15 @@
*/
abstract ProtoStreamReader<T> protoStreamReader();
- AbstractProtoDiskReadWriter(@NonNull File rootDir, long writeDelayMs,
+ AbstractProtoDiskReadWriter(@NonNull File rootDir,
@NonNull ScheduledExecutorService scheduledExecutorService) {
mRootDir = rootDir;
- mWriteDelayMs = writeDelayMs;
mScheduledExecutorService = scheduledExecutorService;
}
@WorkerThread
- void delete(@NonNull String fileName) {
+ synchronized void delete(@NonNull String fileName) {
+ mScheduledFileDataMap.remove(fileName);
final File file = getFile(fileName);
if (!file.exists()) {
return;
@@ -174,7 +177,7 @@
}
mScheduledFuture = mScheduledExecutorService.schedule(this::flushScheduledData,
- mWriteDelayMs, TimeUnit.MILLISECONDS);
+ DEFAULT_DISK_WRITE_DELAY, TimeUnit.MILLISECONDS);
}
/**
@@ -183,7 +186,13 @@
*/
@MainThread
synchronized void saveImmediately(@NonNull String fileName, @NonNull T data) {
- if (mScheduledExecutorService.isShutdown()) {
+ mScheduledFileDataMap.put(fileName, data);
+ triggerScheduledFlushEarly();
+ }
+
+ @MainThread
+ private synchronized void triggerScheduledFlushEarly() {
+ if (mScheduledFileDataMap.isEmpty() || mScheduledExecutorService.isShutdown()) {
return;
}
// Cancel existing future.
@@ -194,7 +203,6 @@
mScheduledFuture.cancel(true);
}
- mScheduledFileDataMap.put(fileName, data);
// Submit flush and blocks until it completes. Blocking will prevent the device from
// shutting down before flushing completes.
Future<?> future = mScheduledExecutorService.submit(this::flushScheduledData);
@@ -212,9 +220,10 @@
return;
}
for (String fileName : mScheduledFileDataMap.keySet()) {
- T data = mScheduledFileDataMap.remove(fileName);
+ T data = mScheduledFileDataMap.get(fileName);
writeTo(fileName, data);
}
+ mScheduledFileDataMap.clear();
mScheduledFuture = null;
}
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 859cdf2..41bc361 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -274,6 +274,10 @@
}
protoOutputStream.write(ConversationInfoProto.SHORTCUT_FLAGS, mShortcutFlags);
protoOutputStream.write(ConversationInfoProto.CONVERSATION_FLAGS, mConversationFlags);
+ if (mContactPhoneNumber != null) {
+ protoOutputStream.write(ConversationInfoProto.CONTACT_PHONE_NUMBER,
+ mContactPhoneNumber);
+ }
}
/** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */
@@ -315,6 +319,10 @@
builder.setConversationFlags(protoInputStream.readInt(
ConversationInfoProto.CONVERSATION_FLAGS));
break;
+ case (int) ConversationInfoProto.CONTACT_PHONE_NUMBER:
+ builder.setContactPhoneNumber(protoInputStream.readString(
+ ConversationInfoProto.CONTACT_PHONE_NUMBER));
+ break;
default:
Slog.w(TAG, "Could not read undefined field: "
+ protoInputStream.getFieldNumber());
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 3afb209..89c4972 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -22,8 +22,6 @@
import android.annotation.WorkerThread;
import android.content.LocusId;
import android.net.Uri;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
@@ -50,8 +48,6 @@
private static final String CONVERSATIONS_FILE_NAME = "conversations";
- private static final long DISK_WRITE_DELAY = 2L * DateUtils.MINUTE_IN_MILLIS;
-
// Shortcut ID -> Conversation Info
@GuardedBy("this")
private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
@@ -74,16 +70,13 @@
private final ScheduledExecutorService mScheduledExecutorService;
private final File mPackageDir;
- private final ContactsQueryHelper mHelper;
private ConversationInfosProtoDiskReadWriter mConversationInfosProtoDiskReadWriter;
ConversationStore(@NonNull File packageDir,
- @NonNull ScheduledExecutorService scheduledExecutorService,
- @NonNull ContactsQueryHelper helper) {
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
mScheduledExecutorService = scheduledExecutorService;
mPackageDir = packageDir;
- mHelper = helper;
}
/**
@@ -92,7 +85,7 @@
*/
@MainThread
void loadConversationsFromDisk() {
- mScheduledExecutorService.submit(() -> {
+ mScheduledExecutorService.execute(() -> {
synchronized (this) {
ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
getConversationInfosProtoDiskReadWriter();
@@ -105,7 +98,6 @@
return;
}
for (ConversationInfo conversationInfo : conversationsOnDisk) {
- conversationInfo = restoreConversationPhoneNumber(conversationInfo);
updateConversationsInMemory(conversationInfo);
}
}
@@ -194,6 +186,15 @@
return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId));
}
+ synchronized void onDestroy() {
+ mConversationInfoMap.clear();
+ mContactUriToShortcutIdMap.clear();
+ mLocusIdToShortcutIdMap.clear();
+ mNotifChannelIdToShortcutIdMap.clear();
+ mPhoneNumberToShortcutIdMap.clear();
+ mConversationInfosProtoDiskReadWriter.deleteConversationsFile();
+ }
+
@MainThread
private synchronized void updateConversationsInMemory(
@NonNull ConversationInfo conversationInfo) {
@@ -239,41 +240,21 @@
}
if (mConversationInfosProtoDiskReadWriter == null) {
mConversationInfosProtoDiskReadWriter = new ConversationInfosProtoDiskReadWriter(
- mPackageDir, CONVERSATIONS_FILE_NAME, DISK_WRITE_DELAY,
- mScheduledExecutorService);
+ mPackageDir, CONVERSATIONS_FILE_NAME, mScheduledExecutorService);
}
return mConversationInfosProtoDiskReadWriter;
}
- /**
- * Conversation's phone number is not saved on disk, so it has to be fetched.
- */
- @WorkerThread
- private ConversationInfo restoreConversationPhoneNumber(
- @NonNull ConversationInfo conversationInfo) {
- if (conversationInfo.getContactUri() != null) {
- if (mHelper.query(conversationInfo.getContactUri().toString())) {
- String phoneNumber = mHelper.getPhoneNumber();
- if (!TextUtils.isEmpty(phoneNumber)) {
- conversationInfo = new ConversationInfo.Builder(
- conversationInfo).setContactPhoneNumber(
- phoneNumber).build();
- }
- }
- }
- return conversationInfo;
- }
-
- /** Reads and writes {@link ConversationInfo} on disk. */
- static class ConversationInfosProtoDiskReadWriter extends
+ /** Reads and writes {@link ConversationInfo}s on disk. */
+ private static class ConversationInfosProtoDiskReadWriter extends
AbstractProtoDiskReadWriter<List<ConversationInfo>> {
private final String mConversationInfoFileName;
- ConversationInfosProtoDiskReadWriter(@NonNull File baseDir,
+ ConversationInfosProtoDiskReadWriter(@NonNull File rootDir,
@NonNull String conversationInfoFileName,
- long writeDelayMs, @NonNull ScheduledExecutorService scheduledExecutorService) {
- super(baseDir, writeDelayMs, scheduledExecutorService);
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
+ super(rootDir, scheduledExecutorService);
mConversationInfoFileName = conversationInfoFileName;
}
@@ -328,5 +309,10 @@
void saveConversationsImmediately(@NonNull List<ConversationInfo> conversationInfos) {
saveImmediately(mConversationInfoFileName, conversationInfos);
}
+
+ @WorkerThread
+ void deleteConversationsFile() {
+ delete(mConversationInfoFileName);
+ }
}
}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 6b97c98..3a34c6a 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -31,7 +31,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
@@ -51,9 +53,9 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -62,11 +64,13 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.SmsApplication;
import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -81,8 +85,8 @@
*/
public class DataManager {
- private static final int MY_UID = Process.myUid();
- private static final int MY_PID = Process.myPid();
+ private static final String TAG = "DataManager";
+
private static final long QUERY_EVENTS_MAX_AGE_MS = DateUtils.DAY_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
@@ -103,6 +107,7 @@
private ShortcutServiceInternal mShortcutServiceInternal;
private PackageManagerInternal mPackageManagerInternal;
+ private NotificationManagerInternal mNotificationManagerInternal;
private UserManager mUserManager;
public DataManager(Context context) {
@@ -121,9 +126,10 @@
public void initialize() {
mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mNotificationManagerInternal = LocalServices.getService(NotificationManagerInternal.class);
mUserManager = mContext.getSystemService(UserManager.class);
- mShortcutServiceInternal.addListener(new ShortcutServiceListener());
+ mShortcutServiceInternal.addShortcutChangeCallback(new ShortcutServiceCallback());
IntentFilter shutdownIntentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
BroadcastReceiver shutdownBroadcastReceiver = new ShutdownBroadcastReceiver();
@@ -134,8 +140,7 @@
public void onUserUnlocked(int userId) {
UserData userData = mUserDataArray.get(userId);
if (userData == null) {
- userData = new UserData(userId, mDiskReadWriterExecutor,
- mInjector.createContactsQueryHelper(mContext));
+ userData = new UserData(userId, mDiskReadWriterExecutor);
mUserDataArray.put(userId, userData);
}
userData.setUserUnlocked();
@@ -275,40 +280,35 @@
mContext.getPackageName(), intentFilter, callingUserId);
}
- /** Reports the {@link AppTargetEvent} from App Prediction Manager. */
- public void reportAppTargetEvent(@NonNull AppTargetEvent event,
+ /** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
+ public void reportShareTargetEvent(@NonNull AppTargetEvent event,
@Nullable IntentFilter intentFilter) {
AppTarget appTarget = event.getTarget();
- ShortcutInfo shortcutInfo = appTarget != null ? appTarget.getShortcutInfo() : null;
- if (shortcutInfo == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
+ if (appTarget == null || event.getAction() != AppTargetEvent.ACTION_LAUNCH) {
return;
}
- PackageData packageData = getPackage(appTarget.getPackageName(),
- appTarget.getUser().getIdentifier());
- if (packageData == null) {
- return;
- }
+ UserData userData = getUnlockedUserData(appTarget.getUser().getIdentifier());
+ PackageData packageData = userData.getOrCreatePackageData(appTarget.getPackageName());
+ String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+ @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
+ EventHistoryImpl eventHistory;
if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
- String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
- String shortcutId = shortcutInfo.getId();
- if (packageData.getConversationStore().getConversation(shortcutId) == null
- || TextUtils.isEmpty(mimeType)) {
+ // Direct share event
+ if (appTarget.getShortcutInfo() == null) {
return;
}
- EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
- EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
- @Event.EventType int eventType;
- if (mimeType.startsWith("text/")) {
- eventType = Event.TYPE_SHARE_TEXT;
- } else if (mimeType.startsWith("image/")) {
- eventType = Event.TYPE_SHARE_IMAGE;
- } else if (mimeType.startsWith("video/")) {
- eventType = Event.TYPE_SHARE_VIDEO;
- } else {
- eventType = Event.TYPE_SHARE_OTHER;
+ String shortcutId = appTarget.getShortcutInfo().getId();
+ if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+ addOrUpdateConversationInfo(appTarget.getShortcutInfo());
}
- eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
+ eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
+ } else {
+ // App share event
+ eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+ EventStore.CATEGORY_CLASS_BASED, appTarget.getClassName());
}
+ eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
}
/** Prunes the data for the specified user. */
@@ -319,12 +319,11 @@
}
pruneUninstalledPackageData(userData);
- long currentTimeMillis = System.currentTimeMillis();
userData.forAllPackages(packageData -> {
if (signal.isCanceled()) {
return;
}
- packageData.getEventStore().pruneOldEvents(currentTimeMillis);
+ packageData.getEventStore().pruneOldEvents();
if (!packageData.isDefaultDialer()) {
packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_CALL);
}
@@ -335,6 +334,17 @@
});
}
+ private int mimeTypeToShareEventType(String mimeType) {
+ if (mimeType.startsWith("text/")) {
+ return Event.TYPE_SHARE_TEXT;
+ } else if (mimeType.startsWith("image/")) {
+ return Event.TYPE_SHARE_IMAGE;
+ } else if (mimeType.startsWith("video/")) {
+ return Event.TYPE_SHARE_VIDEO;
+ }
+ return Event.TYPE_SHARE_OTHER;
+ }
+
private void pruneUninstalledPackageData(@NonNull UserData userData) {
Set<String> installApps = new ArraySet<>();
mPackageManagerInternal.forEachInstalledPackage(
@@ -359,7 +369,7 @@
return mShortcutServiceInternal.getShortcuts(
UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
- /*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID);
+ /*componentName=*/ null, queryFlags, userId, Process.myPid(), Process.myUid());
}
private void forAllUnlockedUsers(Consumer<UserData> consumer) {
@@ -410,12 +420,13 @@
EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
}
+ private boolean isPersonShortcut(@NonNull ShortcutInfo shortcutInfo) {
+ return shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0;
+ }
+
@VisibleForTesting
@WorkerThread
- void onShortcutAddedOrUpdated(@NonNull ShortcutInfo shortcutInfo) {
- if (shortcutInfo.getPersons() == null || shortcutInfo.getPersons().length == 0) {
- return;
- }
+ void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) {
UserData userData = getUnlockedUserData(shortcutInfo.getUserId());
if (userData == null) {
return;
@@ -431,24 +442,24 @@
builder.setShortcutId(shortcutInfo.getId());
builder.setLocusId(shortcutInfo.getLocusId());
builder.setShortcutFlags(shortcutInfo.getFlags());
+ builder.setContactUri(null);
+ builder.setContactPhoneNumber(null);
+ builder.setContactStarred(false);
- Person person = shortcutInfo.getPersons()[0];
- builder.setPersonImportant(person.isImportant());
- builder.setPersonBot(person.isBot());
- String contactUri = person.getUri();
- if (contactUri != null) {
- ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
- if (helper.query(contactUri)) {
- builder.setContactUri(helper.getContactUri());
- builder.setContactStarred(helper.isStarred());
- builder.setContactPhoneNumber(helper.getPhoneNumber());
+ if (shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0) {
+ Person person = shortcutInfo.getPersons()[0];
+ builder.setPersonImportant(person.isImportant());
+ builder.setPersonBot(person.isBot());
+ String contactUri = person.getUri();
+ if (contactUri != null) {
+ ContactsQueryHelper helper = mInjector.createContactsQueryHelper(mContext);
+ if (helper.query(contactUri)) {
+ builder.setContactUri(helper.getContactUri());
+ builder.setContactStarred(helper.isStarred());
+ builder.setContactPhoneNumber(helper.getPhoneNumber());
+ }
}
- } else {
- builder.setContactUri(null);
- builder.setContactPhoneNumber(null);
- builder.setContactStarred(false);
}
-
conversationStore.addOrUpdate(builder.build());
}
@@ -617,16 +628,40 @@
}
/** Listener for the shortcut data changes. */
- private class ShortcutServiceListener implements
- ShortcutServiceInternal.ShortcutChangeListener {
+ private class ShortcutServiceCallback implements LauncherApps.ShortcutChangeCallback {
@Override
- public void onShortcutChanged(@NonNull String packageName, int userId) {
- BackgroundThread.getExecutor().execute(() -> {
- List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
- /*shortcutIds=*/ null);
+ public void onShortcutsAddedOrUpdated(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ mInjector.getBackgroundExecutor().execute(() -> {
for (ShortcutInfo shortcut : shortcuts) {
- onShortcutAddedOrUpdated(shortcut);
+ if (isPersonShortcut(shortcut)) {
+ addOrUpdateConversationInfo(shortcut);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onShortcutsRemoved(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
+ mInjector.getBackgroundExecutor().execute(() -> {
+ int uid = Process.INVALID_UID;
+ try {
+ uid = mContext.getPackageManager().getPackageUidAsUser(
+ packageName, user.getIdentifier());
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Package not found: " + packageName, e);
+ }
+ PackageData packageData = getPackage(packageName, user.getIdentifier());
+ for (ShortcutInfo shortcutInfo : shortcuts) {
+ if (packageData != null) {
+ packageData.deleteDataForConversation(shortcutInfo.getId());
+ }
+ if (uid != Process.INVALID_UID) {
+ mNotificationManagerInternal.onConversationRemoved(
+ shortcutInfo.getPackage(), uid, shortcutInfo.getId());
+ }
}
});
}
@@ -782,6 +817,10 @@
return Executors.newSingleThreadScheduledExecutor();
}
+ Executor getBackgroundExecutor() {
+ return BackgroundThread.getExecutor();
+ }
+
ContactsQueryHelper createContactsQueryHelper(Context context) {
return new ContactsQueryHelper(context);
}
diff --git a/services/people/java/com/android/server/people/data/Event.java b/services/people/java/com/android/server/people/data/Event.java
index 81411c0..a929f6f 100644
--- a/services/people/java/com/android/server/people/data/Event.java
+++ b/services/people/java/com/android/server/people/data/Event.java
@@ -20,7 +20,13 @@
import android.annotation.NonNull;
import android.text.format.DateFormat;
import android.util.ArraySet;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+import com.android.server.people.PeopleEventProto;
+
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -29,6 +35,8 @@
/** An event representing the interaction with a specific conversation or app. */
public class Event {
+ private static final String TAG = Event.class.getSimpleName();
+
public static final int TYPE_SHORTCUT_INVOCATION = 1;
public static final int TYPE_NOTIFICATION_POSTED = 2;
@@ -142,6 +150,36 @@
return mDurationSeconds;
}
+ /** Writes field members to {@link ProtoOutputStream}. */
+ void writeToProto(@NonNull ProtoOutputStream protoOutputStream) {
+ protoOutputStream.write(PeopleEventProto.EVENT_TYPE, mType);
+ protoOutputStream.write(PeopleEventProto.TIME, mTimestamp);
+ protoOutputStream.write(PeopleEventProto.DURATION, mDurationSeconds);
+ }
+
+ /** Reads from {@link ProtoInputStream} and constructs {@link Event}. */
+ @NonNull
+ static Event readFromProto(@NonNull ProtoInputStream protoInputStream) throws IOException {
+ Event.Builder builder = new Event.Builder();
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) PeopleEventProto.EVENT_TYPE:
+ builder.setType(protoInputStream.readInt(PeopleEventProto.EVENT_TYPE));
+ break;
+ case (int) PeopleEventProto.TIME:
+ builder.setTimestamp(protoInputStream.readLong(PeopleEventProto.TIME));
+ break;
+ case (int) PeopleEventProto.DURATION:
+ builder.setDurationSeconds(protoInputStream.readInt(PeopleEventProto.DURATION));
+ break;
+ default:
+ Slog.w(TAG, "Could not read undefined field: "
+ + protoInputStream.getFieldNumber());
+ }
+ }
+ return builder.build();
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -177,12 +215,14 @@
/** Builder class for {@link Event} objects. */
static class Builder {
- private final long mTimestamp;
+ private long mTimestamp;
- private final int mType;
+ private int mType;
private int mDurationSeconds;
+ private Builder() {}
+
Builder(long timestamp, @EventType int type) {
mTimestamp = timestamp;
mType = type;
@@ -193,6 +233,16 @@
return this;
}
+ private Builder setTimestamp(long timestamp) {
+ mTimestamp = timestamp;
+ return this;
+ }
+
+ private Builder setType(int type) {
+ mType = type;
+ return this;
+ }
+
Event build() {
return new Event(this);
}
diff --git a/services/people/java/com/android/server/people/data/EventHistoryImpl.java b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
index 6bef1db..85661c6 100644
--- a/services/people/java/com/android/server/people/data/EventHistoryImpl.java
+++ b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
@@ -16,42 +16,149 @@
package com.android.server.people.data;
+import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.people.PeopleEventIndexesProto;
+import com.android.server.people.PeopleEventsProto;
+import com.android.server.people.TypedPeopleEventIndexProto;
+import com.google.android.collect.Lists;
+
+import java.io.File;
+import java.io.IOException;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+
class EventHistoryImpl implements EventHistory {
+ private static final long MAX_EVENTS_AGE = 4L * DateUtils.HOUR_IN_MILLIS;
+ private static final long PRUNE_OLD_EVENTS_DELAY = 15L * DateUtils.MINUTE_IN_MILLIS;
+
+ private static final String EVENTS_DIR = "events";
+ private static final String INDEXES_DIR = "indexes";
+
private final Injector mInjector;
+ private final ScheduledExecutorService mScheduledExecutorService;
+ private final EventsProtoDiskReadWriter mEventsProtoDiskReadWriter;
+ private final EventIndexesProtoDiskReadWriter mEventIndexesProtoDiskReadWriter;
// Event Type -> Event Index
+ @GuardedBy("this")
private final SparseArray<EventIndex> mEventIndexArray = new SparseArray<>();
+ @GuardedBy("this")
private final EventList mRecentEvents = new EventList();
- EventHistoryImpl() {
- mInjector = new Injector();
+ private long mLastPruneTime;
+
+ EventHistoryImpl(@NonNull File rootDir,
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
+ this(new Injector(), rootDir, scheduledExecutorService);
}
@VisibleForTesting
- EventHistoryImpl(Injector injector) {
+ EventHistoryImpl(@NonNull Injector injector, @NonNull File rootDir,
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
mInjector = injector;
+ mScheduledExecutorService = scheduledExecutorService;
+ mLastPruneTime = injector.currentTimeMillis();
+
+ File eventsDir = new File(rootDir, EVENTS_DIR);
+ mEventsProtoDiskReadWriter = new EventsProtoDiskReadWriter(eventsDir,
+ mScheduledExecutorService);
+ File indexesDir = new File(rootDir, INDEXES_DIR);
+ mEventIndexesProtoDiskReadWriter = new EventIndexesProtoDiskReadWriter(indexesDir,
+ scheduledExecutorService);
+ }
+
+ @WorkerThread
+ @NonNull
+ static Map<String, EventHistoryImpl> eventHistoriesImplFromDisk(File categoryDir,
+ ScheduledExecutorService scheduledExecutorService) {
+ return eventHistoriesImplFromDisk(new Injector(), categoryDir, scheduledExecutorService);
+ }
+
+ @VisibleForTesting
+ @NonNull
+ static Map<String, EventHistoryImpl> eventHistoriesImplFromDisk(Injector injector,
+ File categoryDir, ScheduledExecutorService scheduledExecutorService) {
+ Map<String, EventHistoryImpl> results = new ArrayMap<>();
+ File[] keyDirs = categoryDir.listFiles(File::isDirectory);
+ if (keyDirs == null) {
+ return results;
+ }
+ for (File keyDir : keyDirs) {
+ File[] dirContents = keyDir.listFiles(
+ (dir, name) -> EVENTS_DIR.equals(name) || INDEXES_DIR.equals(name));
+ if (dirContents != null && dirContents.length == 2) {
+ EventHistoryImpl eventHistory = new EventHistoryImpl(injector, keyDir,
+ scheduledExecutorService);
+ eventHistory.loadFromDisk();
+ results.put(Uri.decode(keyDir.getName()), eventHistory);
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Loads recent events and indexes from disk to memory in a background thread. This should be
+ * called after the device powers on and the user has been unlocked.
+ */
+ @VisibleForTesting
+ @MainThread
+ synchronized void loadFromDisk() {
+ mScheduledExecutorService.execute(() -> {
+ synchronized (this) {
+ EventList diskEvents = mEventsProtoDiskReadWriter.loadRecentEventsFromDisk();
+ if (diskEvents != null) {
+ diskEvents.removeOldEvents(mInjector.currentTimeMillis() - MAX_EVENTS_AGE);
+ mRecentEvents.addAll(diskEvents.getAllEvents());
+ }
+
+ SparseArray<EventIndex> diskIndexes =
+ mEventIndexesProtoDiskReadWriter.loadIndexesFromDisk();
+ if (diskIndexes != null) {
+ for (int i = 0; i < diskIndexes.size(); i++) {
+ mEventIndexArray.put(diskIndexes.keyAt(i), diskIndexes.valueAt(i));
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Flushes events and indexes immediately. This should be called when device is powering off.
+ */
+ @MainThread
+ synchronized void saveToDisk() {
+ mEventsProtoDiskReadWriter.saveEventsImmediately(mRecentEvents);
+ mEventIndexesProtoDiskReadWriter.saveIndexesImmediately(mEventIndexArray);
}
@Override
@NonNull
- public EventIndex getEventIndex(@Event.EventType int eventType) {
+ public synchronized EventIndex getEventIndex(@Event.EventType int eventType) {
EventIndex eventIndex = mEventIndexArray.get(eventType);
return eventIndex != null ? new EventIndex(eventIndex) : mInjector.createEventIndex();
}
@Override
@NonNull
- public EventIndex getEventIndex(Set<Integer> eventTypes) {
+ public synchronized EventIndex getEventIndex(Set<Integer> eventTypes) {
EventIndex combined = mInjector.createEventIndex();
for (@Event.EventType int eventType : eventTypes) {
EventIndex eventIndex = mEventIndexArray.get(eventType);
@@ -64,11 +171,35 @@
@Override
@NonNull
- public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) {
+ public synchronized List<Event> queryEvents(Set<Integer> eventTypes, long startTime,
+ long endTime) {
return mRecentEvents.queryEvents(eventTypes, startTime, endTime);
}
- void addEvent(Event event) {
+ synchronized void addEvent(Event event) {
+ pruneOldEvents();
+ addEventInMemory(event);
+ mEventsProtoDiskReadWriter.scheduleEventsSave(mRecentEvents);
+ mEventIndexesProtoDiskReadWriter.scheduleIndexesSave(mEventIndexArray);
+ }
+
+ synchronized void onDestroy() {
+ mEventIndexArray.clear();
+ mRecentEvents.clear();
+ mEventsProtoDiskReadWriter.deleteRecentEventsFile();
+ mEventIndexesProtoDiskReadWriter.deleteIndexesFile();
+ }
+
+ /** Deletes the events data that exceeds the retention period. */
+ synchronized void pruneOldEvents() {
+ long currentTime = mInjector.currentTimeMillis();
+ if (currentTime - mLastPruneTime > PRUNE_OLD_EVENTS_DELAY) {
+ mRecentEvents.removeOldEvents(currentTime - MAX_EVENTS_AGE);
+ mLastPruneTime = currentTime;
+ }
+ }
+
+ private synchronized void addEventInMemory(Event event) {
EventIndex eventIndex = mEventIndexArray.get(event.getType());
if (eventIndex == null) {
eventIndex = mInjector.createEventIndex();
@@ -78,22 +209,180 @@
mRecentEvents.add(event);
}
- void onDestroy() {
- mEventIndexArray.clear();
- mRecentEvents.clear();
- // TODO: STOPSHIP: Delete the data files.
- }
-
- /** Deletes the events data that exceeds the retention period. */
- void pruneOldEvents(long currentTimeMillis) {
- // TODO: STOPSHIP: Delete the old events data files.
- }
-
@VisibleForTesting
static class Injector {
EventIndex createEventIndex() {
return new EventIndex();
}
+
+ long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+ }
+
+ /** Reads and writes {@link Event}s on disk. */
+ private static class EventsProtoDiskReadWriter extends AbstractProtoDiskReadWriter<EventList> {
+
+ private static final String TAG = EventsProtoDiskReadWriter.class.getSimpleName();
+
+ private static final String RECENT_FILE = "recent";
+
+
+ EventsProtoDiskReadWriter(@NonNull File rootDir,
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
+ super(rootDir, scheduledExecutorService);
+ rootDir.mkdirs();
+ }
+
+ @Override
+ ProtoStreamWriter<EventList> protoStreamWriter() {
+ return (protoOutputStream, data) -> {
+ for (Event event : data.getAllEvents()) {
+ long token = protoOutputStream.start(PeopleEventsProto.EVENTS);
+ event.writeToProto(protoOutputStream);
+ protoOutputStream.end(token);
+ }
+ };
+ }
+
+ @Override
+ ProtoStreamReader<EventList> protoStreamReader() {
+ return protoInputStream -> {
+ List<Event> results = Lists.newArrayList();
+ try {
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (protoInputStream.getFieldNumber() != (int) PeopleEventsProto.EVENTS) {
+ continue;
+ }
+ long token = protoInputStream.start(PeopleEventsProto.EVENTS);
+ Event event = Event.readFromProto(protoInputStream);
+ protoInputStream.end(token);
+ results.add(event);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read protobuf input stream.", e);
+ }
+ EventList eventList = new EventList();
+ eventList.addAll(results);
+ return eventList;
+ };
+ }
+
+ @MainThread
+ void scheduleEventsSave(EventList recentEvents) {
+ scheduleSave(RECENT_FILE, recentEvents);
+ }
+
+ @MainThread
+ void saveEventsImmediately(EventList recentEvents) {
+ saveImmediately(RECENT_FILE, recentEvents);
+ }
+
+ /**
+ * Loads recent events from disk. This should be called when device is powered on.
+ */
+ @WorkerThread
+ @Nullable
+ EventList loadRecentEventsFromDisk() {
+ return read(RECENT_FILE);
+ }
+
+ @WorkerThread
+ void deleteRecentEventsFile() {
+ delete(RECENT_FILE);
+ }
+ }
+
+ /** Reads and writes {@link EventIndex}s on disk. */
+ private static class EventIndexesProtoDiskReadWriter extends
+ AbstractProtoDiskReadWriter<SparseArray<EventIndex>> {
+
+ private static final String TAG = EventIndexesProtoDiskReadWriter.class.getSimpleName();
+
+ private static final String INDEXES_FILE = "index";
+
+ EventIndexesProtoDiskReadWriter(@NonNull File rootDir,
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
+ super(rootDir, scheduledExecutorService);
+ rootDir.mkdirs();
+ }
+
+ @Override
+ ProtoStreamWriter<SparseArray<EventIndex>> protoStreamWriter() {
+ return (protoOutputStream, data) -> {
+ for (int i = 0; i < data.size(); i++) {
+ @Event.EventType int eventType = data.keyAt(i);
+ EventIndex index = data.valueAt(i);
+ long token = protoOutputStream.start(PeopleEventIndexesProto.TYPED_INDEXES);
+ protoOutputStream.write(TypedPeopleEventIndexProto.EVENT_TYPE, eventType);
+ long indexToken = protoOutputStream.start(TypedPeopleEventIndexProto.INDEX);
+ index.writeToProto(protoOutputStream);
+ protoOutputStream.end(indexToken);
+ protoOutputStream.end(token);
+ }
+ };
+ }
+
+ @Override
+ ProtoStreamReader<SparseArray<EventIndex>> protoStreamReader() {
+ return protoInputStream -> {
+ SparseArray<EventIndex> results = new SparseArray<>();
+ try {
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (protoInputStream.getFieldNumber()
+ != (int) PeopleEventIndexesProto.TYPED_INDEXES) {
+ continue;
+ }
+ long token = protoInputStream.start(PeopleEventIndexesProto.TYPED_INDEXES);
+ @Event.EventType int eventType = 0;
+ EventIndex index = EventIndex.EMPTY;
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) TypedPeopleEventIndexProto.EVENT_TYPE:
+ eventType = protoInputStream.readInt(
+ TypedPeopleEventIndexProto.EVENT_TYPE);
+ break;
+ case (int) TypedPeopleEventIndexProto.INDEX:
+ long indexToken = protoInputStream.start(
+ TypedPeopleEventIndexProto.INDEX);
+ index = EventIndex.readFromProto(protoInputStream);
+ protoInputStream.end(indexToken);
+ break;
+ default:
+ Slog.w(TAG, "Could not read undefined field: "
+ + protoInputStream.getFieldNumber());
+ }
+ }
+ results.append(eventType, index);
+ protoInputStream.end(token);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read protobuf input stream.", e);
+ }
+ return results;
+ };
+ }
+
+ @MainThread
+ void scheduleIndexesSave(SparseArray<EventIndex> indexes) {
+ scheduleSave(INDEXES_FILE, indexes);
+ }
+
+ @MainThread
+ void saveIndexesImmediately(SparseArray<EventIndex> indexes) {
+ saveImmediately(INDEXES_FILE, indexes);
+ }
+
+ @WorkerThread
+ @Nullable
+ SparseArray<EventIndex> loadIndexesFromDisk() {
+ return read(INDEXES_FILE);
+ }
+
+ @WorkerThread
+ void deleteIndexesFile() {
+ delete(INDEXES_FILE);
+ }
}
}
diff --git a/services/people/java/com/android/server/people/data/EventIndex.java b/services/people/java/com/android/server/people/data/EventIndex.java
index b74a3fa..47b6207 100644
--- a/services/people/java/com/android/server/people/data/EventIndex.java
+++ b/services/people/java/com/android/server/people/data/EventIndex.java
@@ -21,9 +21,14 @@
import android.annotation.Nullable;
import android.text.format.DateFormat;
import android.util.Range;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.people.PeopleEventIndexProto;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
@@ -34,6 +39,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.TimeZone;
import java.util.function.Function;
@@ -60,8 +66,9 @@
* </pre>
*/
public class EventIndex {
+ private static final String TAG = EventIndex.class.getSimpleName();
- private static final int LONG_SIZE_BITS = 64;
+ private static final int RETENTION_DAYS = 63;
private static final int TIME_SLOT_ONE_DAY = 0;
@@ -118,22 +125,23 @@
private final Injector mInjector;
EventIndex() {
- mInjector = new Injector();
- mEventBitmaps = new long[]{0L, 0L, 0L, 0L};
- mLastUpdatedTime = mInjector.currentTimeMillis();
+ this(new Injector());
}
- EventIndex(EventIndex from) {
- mInjector = new Injector();
- mEventBitmaps = Arrays.copyOf(from.mEventBitmaps, TIME_SLOT_TYPES_COUNT);
- mLastUpdatedTime = from.mLastUpdatedTime;
+ EventIndex(@NonNull EventIndex from) {
+ this(from.mInjector, Arrays.copyOf(from.mEventBitmaps, TIME_SLOT_TYPES_COUNT),
+ from.mLastUpdatedTime);
}
@VisibleForTesting
- EventIndex(Injector injector) {
+ EventIndex(@NonNull Injector injector) {
+ this(injector, new long[]{0L, 0L, 0L, 0L}, injector.currentTimeMillis());
+ }
+
+ private EventIndex(@NonNull Injector injector, long[] eventBitmaps, long lastUpdatedTime) {
mInjector = injector;
- mEventBitmaps = new long[]{0L, 0L, 0L, 0L};
- mLastUpdatedTime = mInjector.currentTimeMillis();
+ mEventBitmaps = eventBitmaps;
+ mLastUpdatedTime = lastUpdatedTime;
}
/**
@@ -202,7 +210,7 @@
updateEventBitmaps(currentTime);
for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
int offset = diffTimeSlots(slotType, eventTime, currentTime);
- if (offset < LONG_SIZE_BITS) {
+ if (offset < Long.SIZE) {
mEventBitmaps[slotType] |= (1L << offset);
}
}
@@ -232,19 +240,70 @@
return sb.toString();
}
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof EventIndex)) {
+ return false;
+ }
+ EventIndex other = (EventIndex) obj;
+ return mLastUpdatedTime == other.mLastUpdatedTime
+ && Arrays.equals(mEventBitmaps, other.mEventBitmaps);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLastUpdatedTime, mEventBitmaps);
+ }
+
+ synchronized void writeToProto(@NonNull ProtoOutputStream protoOutputStream) {
+ for (long bitmap : mEventBitmaps) {
+ protoOutputStream.write(PeopleEventIndexProto.EVENT_BITMAPS, bitmap);
+ }
+ protoOutputStream.write(PeopleEventIndexProto.LAST_UPDATED_TIME, mLastUpdatedTime);
+ }
+
/** Shifts the event bitmaps to make them up-to-date. */
private void updateEventBitmaps(long currentTimeMillis) {
for (int slotType = 0; slotType < TIME_SLOT_TYPES_COUNT; slotType++) {
int offset = diffTimeSlots(slotType, mLastUpdatedTime, currentTimeMillis);
- if (offset < LONG_SIZE_BITS) {
+ if (offset < Long.SIZE) {
mEventBitmaps[slotType] <<= offset;
} else {
mEventBitmaps[slotType] = 0L;
}
}
+
+ int bitsToClear = Long.SIZE - RETENTION_DAYS;
+ mEventBitmaps[TIME_SLOT_ONE_DAY] <<= bitsToClear;
+ mEventBitmaps[TIME_SLOT_ONE_DAY] >>>= bitsToClear;
mLastUpdatedTime = currentTimeMillis;
}
+ static EventIndex readFromProto(@NonNull ProtoInputStream protoInputStream) throws IOException {
+ int bitmapIndex = 0;
+ long[] eventBitmaps = new long[TIME_SLOT_TYPES_COUNT];
+ long lastUpdated = 0L;
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) PeopleEventIndexProto.EVENT_BITMAPS:
+ eventBitmaps[bitmapIndex++] = protoInputStream.readLong(
+ PeopleEventIndexProto.EVENT_BITMAPS);
+ break;
+ case (int) PeopleEventIndexProto.LAST_UPDATED_TIME:
+ lastUpdated = protoInputStream.readLong(
+ PeopleEventIndexProto.LAST_UPDATED_TIME);
+ break;
+ default:
+ Slog.e(TAG, "Could not read undefined field: "
+ + protoInputStream.getFieldNumber());
+ }
+ }
+ return new EventIndex(new Injector(), eventBitmaps, lastUpdated);
+ }
+
private static LocalDateTime toLocalDateTime(long epochMilli) {
return LocalDateTime.ofInstant(
Instant.ofEpochMilli(epochMilli), TimeZone.getDefault().toZoneId());
diff --git a/services/people/java/com/android/server/people/data/EventList.java b/services/people/java/com/android/server/people/data/EventList.java
index d770f91..3788d6c 100644
--- a/services/people/java/com/android/server/people/data/EventList.java
+++ b/services/people/java/com/android/server/people/data/EventList.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
+import com.android.internal.util.CollectionUtils;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -41,6 +43,16 @@
mEvents.add(index, event);
}
+
+ /**
+ * Call #add on each event to keep the order.
+ */
+ void addAll(@NonNull List<Event> events) {
+ for (Event event : events) {
+ add(event);
+ }
+ }
+
/**
* Returns a {@link List} of {@link Event}s whose timestamps are between the specified {@code
* fromTimestamp}, inclusive, and {@code toTimestamp} exclusive, and match the specified event
@@ -73,6 +85,44 @@
mEvents.clear();
}
+ /**
+ * Returns a copy of events.
+ */
+ @NonNull
+ List<Event> getAllEvents() {
+ return CollectionUtils.copyOf(mEvents);
+ }
+
+ /**
+ * Remove events that are older than the specified cut off threshold timestamp.
+ */
+ void removeOldEvents(long cutOffThreshold) {
+
+ // Everything before the cut off is considered old, and should be removed.
+ int cutOffIndex = firstIndexOnOrAfter(cutOffThreshold);
+ if (cutOffIndex == 0) {
+ return;
+ }
+
+ // Clear entire list if the cut off is greater than the last element.
+ int eventsSize = mEvents.size();
+ if (cutOffIndex == eventsSize) {
+ mEvents.clear();
+ return;
+ }
+
+ // Reorder the list starting from the cut off index.
+ int i = 0;
+ for (; cutOffIndex < eventsSize; i++, cutOffIndex++) {
+ mEvents.set(i, mEvents.get(cutOffIndex));
+ }
+
+ // Clear the list after reordering.
+ if (eventsSize > i) {
+ mEvents.subList(i, eventsSize).clear();
+ }
+ }
+
/** Returns the first index whose timestamp is greater or equal to the provided timestamp. */
private int firstIndexOnOrAfter(long timestamp) {
int result = mEvents.size();
diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java
index c8d44ac..00d4241 100644
--- a/services/people/java/com/android/server/people/data/EventStore.java
+++ b/services/people/java/com/android/server/people/data/EventStore.java
@@ -17,16 +17,22 @@
package com.android.server.people.data;
import android.annotation.IntDef;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.Uri;
import android.util.ArrayMap;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Predicate;
/** The store that stores and accesses the events data for a package. */
@@ -57,14 +63,58 @@
@Retention(RetentionPolicy.SOURCE)
@interface EventCategory {}
+ @GuardedBy("this")
private final List<Map<String, EventHistoryImpl>> mEventHistoryMaps = new ArrayList<>();
+ private final List<File> mEventsCategoryDirs = new ArrayList<>();
+ private final ScheduledExecutorService mScheduledExecutorService;
- EventStore() {
+ EventStore(@NonNull File packageDir,
+ @NonNull ScheduledExecutorService scheduledExecutorService) {
mEventHistoryMaps.add(CATEGORY_SHORTCUT_BASED, new ArrayMap<>());
mEventHistoryMaps.add(CATEGORY_LOCUS_ID_BASED, new ArrayMap<>());
mEventHistoryMaps.add(CATEGORY_CALL, new ArrayMap<>());
mEventHistoryMaps.add(CATEGORY_SMS, new ArrayMap<>());
mEventHistoryMaps.add(CATEGORY_CLASS_BASED, new ArrayMap<>());
+
+ File eventDir = new File(packageDir, "event");
+ mEventsCategoryDirs.add(CATEGORY_SHORTCUT_BASED, new File(eventDir, "shortcut"));
+ mEventsCategoryDirs.add(CATEGORY_LOCUS_ID_BASED, new File(eventDir, "locus"));
+ mEventsCategoryDirs.add(CATEGORY_CALL, new File(eventDir, "call"));
+ mEventsCategoryDirs.add(CATEGORY_SMS, new File(eventDir, "sms"));
+ mEventsCategoryDirs.add(CATEGORY_CLASS_BASED, new File(eventDir, "class"));
+
+ mScheduledExecutorService = scheduledExecutorService;
+ }
+
+ /**
+ * Loads existing {@link EventHistoryImpl}s from disk. This should be called when device powers
+ * on and user is unlocked.
+ */
+ @MainThread
+ void loadFromDisk() {
+ mScheduledExecutorService.execute(() -> {
+ synchronized (this) {
+ for (@EventCategory int category = 0; category < mEventsCategoryDirs.size();
+ category++) {
+ File categoryDir = mEventsCategoryDirs.get(category);
+ Map<String, EventHistoryImpl> existingEventHistoriesImpl =
+ EventHistoryImpl.eventHistoriesImplFromDisk(categoryDir,
+ mScheduledExecutorService);
+ mEventHistoryMaps.get(category).putAll(existingEventHistoriesImpl);
+ }
+ }
+ });
+ }
+
+ /**
+ * Flushes all {@link EventHistoryImpl}s to disk. Should be called when device is shutting down.
+ */
+ synchronized void saveToDisk() {
+ for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) {
+ for (EventHistoryImpl eventHistory : map.values()) {
+ eventHistory.saveToDisk();
+ }
+ }
}
/**
@@ -74,7 +124,7 @@
* name.
*/
@Nullable
- EventHistory getEventHistory(@EventCategory int category, String key) {
+ synchronized EventHistory getEventHistory(@EventCategory int category, String key) {
return mEventHistoryMaps.get(category).get(key);
}
@@ -87,8 +137,11 @@
* name.
*/
@NonNull
- EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
- return mEventHistoryMaps.get(category).computeIfAbsent(key, k -> new EventHistoryImpl());
+ synchronized EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+ return mEventHistoryMaps.get(category).computeIfAbsent(key,
+ k -> new EventHistoryImpl(
+ new File(mEventsCategoryDirs.get(category), Uri.encode(key)),
+ mScheduledExecutorService));
}
/**
@@ -97,7 +150,7 @@
* @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
* name.
*/
- void deleteEventHistory(@EventCategory int category, String key) {
+ synchronized void deleteEventHistory(@EventCategory int category, String key) {
EventHistoryImpl eventHistory = mEventHistoryMaps.get(category).remove(key);
if (eventHistory != null) {
eventHistory.onDestroy();
@@ -105,16 +158,18 @@
}
/** Deletes all the events and index data for the specified category from disk. */
- void deleteEventHistories(@EventCategory int category) {
+ synchronized void deleteEventHistories(@EventCategory int category) {
+ for (EventHistoryImpl eventHistory : mEventHistoryMaps.get(category).values()) {
+ eventHistory.onDestroy();
+ }
mEventHistoryMaps.get(category).clear();
- // TODO: Implement this method to delete the data from disk.
}
/** Deletes the events data that exceeds the retention period. */
- void pruneOldEvents(long currentTimeMillis) {
+ synchronized void pruneOldEvents() {
for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) {
for (EventHistoryImpl eventHistory : map.values()) {
- eventHistory.pruneOldEvents(currentTimeMillis);
+ eventHistory.pruneOldEvents();
}
}
}
@@ -125,7 +180,8 @@
*
* @param keyChecker Check whether there exists a conversation contains this key.
*/
- void pruneOrphanEventHistories(@EventCategory int category, Predicate<String> keyChecker) {
+ synchronized void pruneOrphanEventHistories(@EventCategory int category,
+ Predicate<String> keyChecker) {
Set<String> keys = mEventHistoryMaps.get(category).keySet();
List<String> keysToDelete = new ArrayList<>();
for (String key : keys) {
@@ -141,4 +197,12 @@
}
}
}
+
+ synchronized void onDestroy() {
+ for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) {
+ for (EventHistoryImpl eventHistory : map.values()) {
+ eventHistory.onDestroy();
+ }
+ }
+ }
}
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index c55f972..35d245f 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -27,8 +27,10 @@
import android.annotation.UserIdInt;
import android.content.LocusId;
import android.text.TextUtils;
+import android.util.ArrayMap;
import java.io.File;
+import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -57,28 +59,53 @@
@NonNull Predicate<String> isDefaultDialerPredicate,
@NonNull Predicate<String> isDefaultSmsAppPredicate,
@NonNull ScheduledExecutorService scheduledExecutorService,
- @NonNull File perUserPeopleDataDir,
- @NonNull ContactsQueryHelper helper) {
+ @NonNull File perUserPeopleDataDir) {
mPackageName = packageName;
mUserId = userId;
mPackageDataDir = new File(perUserPeopleDataDir, mPackageName);
- mConversationStore = new ConversationStore(mPackageDataDir, scheduledExecutorService,
- helper);
- mEventStore = new EventStore();
+ mPackageDataDir.mkdirs();
+
+ mConversationStore = new ConversationStore(mPackageDataDir, scheduledExecutorService);
+ mEventStore = new EventStore(mPackageDataDir, scheduledExecutorService);
mIsDefaultDialerPredicate = isDefaultDialerPredicate;
mIsDefaultSmsAppPredicate = isDefaultSmsAppPredicate;
}
- /** Called when user is unlocked. */
- void loadFromDisk() {
- mPackageDataDir.mkdirs();
+ /**
+ * Returns a map of package directory names as keys and their associated {@link PackageData}.
+ * This should be called when device is powered on and unlocked.
+ */
+ @NonNull
+ static Map<String, PackageData> packagesDataFromDisk(@UserIdInt int userId,
+ @NonNull Predicate<String> isDefaultDialerPredicate,
+ @NonNull Predicate<String> isDefaultSmsAppPredicate,
+ @NonNull ScheduledExecutorService scheduledExecutorService,
+ @NonNull File perUserPeopleDataDir) {
+ Map<String, PackageData> results = new ArrayMap<>();
+ File[] packageDirs = perUserPeopleDataDir.listFiles(File::isDirectory);
+ if (packageDirs == null) {
+ return results;
+ }
+ for (File packageDir : packageDirs) {
+ PackageData packageData = new PackageData(packageDir.getName(), userId,
+ isDefaultDialerPredicate, isDefaultSmsAppPredicate, scheduledExecutorService,
+ perUserPeopleDataDir);
+ packageData.loadFromDisk();
+ results.put(packageDir.getName(), packageData);
+ }
+ return results;
+ }
+
+ private void loadFromDisk() {
mConversationStore.loadConversationsFromDisk();
+ mEventStore.loadFromDisk();
}
/** Called when device is shutting down. */
void saveToDisk() {
mConversationStore.saveConversationsToDisk();
+ mEventStore.saveToDisk();
}
@NonNull
@@ -222,6 +249,7 @@
}
void onDestroy() {
- // TODO: STOPSHIP: Implements this method for the case of package being uninstalled.
+ mEventStore.onDestroy();
+ mConversationStore.onDestroy();
}
}
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index 7ca4b6c..0f8b91b 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -37,8 +37,6 @@
private final ScheduledExecutorService mScheduledExecutorService;
- private final ContactsQueryHelper mHelper;
-
private boolean mIsUnlocked;
private Map<String, PackageData> mPackageDataMap = new ArrayMap<>();
@@ -49,12 +47,10 @@
@Nullable
private String mDefaultSmsApp;
- UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService,
- ContactsQueryHelper helper) {
+ UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService) {
mUserId = userId;
mPerUserPeopleDataDir = new File(Environment.getDataSystemCeDirectory(mUserId), "people");
mScheduledExecutorService = scheduledExecutorService;
- mHelper = helper;
}
@UserIdInt int getUserId() {
@@ -73,9 +69,8 @@
// Ensures per user root directory for people data is present, and attempt to load
// data from disk.
mPerUserPeopleDataDir.mkdirs();
- for (PackageData packageData : mPackageDataMap.values()) {
- packageData.loadFromDisk();
- }
+ mPackageDataMap.putAll(PackageData.packagesDataFromDisk(mUserId, this::isDefaultDialer,
+ this::isDefaultSmsApp, mScheduledExecutorService, mPerUserPeopleDataDir));
}
void setUserStopped() {
@@ -132,7 +127,7 @@
private PackageData createPackageData(String packageName) {
return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
- mScheduledExecutorService, mPerUserPeopleDataDir, mHelper);
+ mScheduledExecutorService, mPerUserPeopleDataDir);
}
private boolean isDefaultDialer(String packageName) {
diff --git a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
index 19cf8af..c89dadc 100644
--- a/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/AppTargetPredictor.java
@@ -73,6 +73,7 @@
*/
@MainThread
public void onAppTargetEvent(AppTargetEvent event) {
+ mCallbackExecutor.execute(() -> reportAppTargetEvent(event));
}
/**
@@ -104,6 +105,11 @@
return mUpdatePredictionsMethod;
}
+ /** To be overridden by the subclass to report app target event. */
+ @WorkerThread
+ void reportAppTargetEvent(AppTargetEvent event) {
+ }
+
/** To be overridden by the subclass to predict the targets. */
@WorkerThread
void predictTargets() {
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 90d8216..8e5d75b 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -16,7 +16,6 @@
package com.android.server.people.prediction;
-import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -28,15 +27,18 @@
import android.content.IntentFilter;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.util.Range;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ChooserActivity;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
+import com.android.server.people.data.Event;
import com.android.server.people.data.EventHistory;
import com.android.server.people.data.PackageData;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -52,89 +54,139 @@
ChooserActivity.APP_PREDICTION_INTENT_FILTER_KEY);
}
- @MainThread
- @Override
- public void onAppTargetEvent(AppTargetEvent event) {
- getDataManager().reportAppTargetEvent(event, mIntentFilter);
- }
-
+ /** Reports chosen history of direct/app share targets. */
@WorkerThread
@Override
- protected void predictTargets() {
- List<ShareTarget> shareTargets = getShareTargets();
- // TODO: Rank the share targets with the data in ShareTarget.mConversationData.
- List<AppTarget> appTargets = new ArrayList<>();
- for (ShareTarget shareTarget : shareTargets) {
-
- ShortcutInfo shortcutInfo = shareTarget.getShareShortcutInfo().getShortcutInfo();
- AppTargetId appTargetId = new AppTargetId(shortcutInfo.getId());
- String shareTargetClassName =
- shareTarget.getShareShortcutInfo().getTargetComponent().getClassName();
- AppTarget appTarget = new AppTarget.Builder(appTargetId, shortcutInfo)
- .setClassName(shareTargetClassName)
- .build();
- appTargets.add(appTarget);
- if (appTargets.size() >= getPredictionContext().getPredictedTargetCount()) {
- break;
- }
- }
- updatePredictions(appTargets);
+ void reportAppTargetEvent(AppTargetEvent event) {
+ getDataManager().reportShareTargetEvent(event, mIntentFilter);
}
- @VisibleForTesting
- List<ShareTarget> getShareTargets() {
+ /** Provides prediction on direct share targets */
+ @WorkerThread
+ @Override
+ void predictTargets() {
+ List<ShareTarget> shareTargets = getDirectShareTargets();
+ rankTargets(shareTargets);
+ List<AppTarget> res = new ArrayList<>();
+ for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
+ shareTargets.size()); i++) {
+ res.add(shareTargets.get(i).getAppTarget());
+ }
+ updatePredictions(res);
+ }
+
+ /** Provides prediction on app share targets */
+ @WorkerThread
+ @Override
+ void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+ List<ShareTarget> shareTargets = getAppShareTargets(targets);
+ rankTargets(shareTargets);
+ List<AppTarget> appTargetList = new ArrayList<>();
+ shareTargets.forEach(t -> appTargetList.add(t.getAppTarget()));
+ callback.accept(appTargetList);
+ }
+
+ private void rankTargets(List<ShareTarget> shareTargets) {
+ // Rank targets based on recency of sharing history only for the moment.
+ // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app.
+ Collections.sort(shareTargets, (t1, t2) -> {
+ if (t1.getEventHistory() == null) {
+ return 1;
+ }
+ if (t2.getEventHistory() == null) {
+ return -1;
+ }
+ Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex(
+ Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+ Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex(
+ Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+ if (timeSlot1 == null) {
+ return 1;
+ } else if (timeSlot2 == null) {
+ return -1;
+ } else {
+ return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper());
+ }
+ });
+ }
+
+ private List<ShareTarget> getDirectShareTargets() {
List<ShareTarget> shareTargets = new ArrayList<>();
List<ShareShortcutInfo> shareShortcuts =
getDataManager().getShareShortcuts(mIntentFilter, mCallingUserId);
for (ShareShortcutInfo shareShortcut : shareShortcuts) {
ShortcutInfo shortcutInfo = shareShortcut.getShortcutInfo();
+ AppTarget appTarget = new AppTarget.Builder(
+ new AppTargetId(shortcutInfo.getId()),
+ shortcutInfo)
+ .setClassName(shareShortcut.getTargetComponent().getClassName())
+ .build();
String packageName = shortcutInfo.getPackage();
int userId = shortcutInfo.getUserId();
PackageData packageData = getDataManager().getPackage(packageName, userId);
- ConversationData conversationData = null;
+ ConversationInfo conversationInfo = null;
+ EventHistory eventHistory = null;
if (packageData != null) {
String shortcutId = shortcutInfo.getId();
- ConversationInfo conversationInfo =
- packageData.getConversationInfo(shortcutId);
-
+ conversationInfo = packageData.getConversationInfo(shortcutId);
if (conversationInfo != null) {
- EventHistory eventHistory = packageData.getEventHistory(shortcutId);
- conversationData = new ConversationData(
- packageName, userId, conversationInfo, eventHistory);
+ eventHistory = packageData.getEventHistory(shortcutId);
}
}
- shareTargets.add(new ShareTarget(shareShortcut, conversationData));
+ shareTargets.add(new ShareTarget(appTarget, eventHistory, conversationInfo));
}
return shareTargets;
}
+ private List<ShareTarget> getAppShareTargets(List<AppTarget> targets) {
+ List<ShareTarget> shareTargets = new ArrayList<>();
+ for (AppTarget target : targets) {
+ PackageData packageData = getDataManager().getPackage(target.getPackageName(),
+ target.getUser().getIdentifier());
+ shareTargets.add(new ShareTarget(target,
+ packageData == null ? null
+ : packageData.getClassLevelEventHistory(target.getClassName()), null));
+ }
+ return shareTargets;
+ }
+
@VisibleForTesting
static class ShareTarget {
@NonNull
- private final ShareShortcutInfo mShareShortcutInfo;
+ private final AppTarget mAppTarget;
@Nullable
- private final ConversationData mConversationData;
+ private final EventHistory mEventHistory;
+ @Nullable
+ private final ConversationInfo mConversationInfo;
- private ShareTarget(@NonNull ShareShortcutInfo shareShortcutInfo,
- @Nullable ConversationData conversationData) {
- mShareShortcutInfo = shareShortcutInfo;
- mConversationData = conversationData;
+ private ShareTarget(@NonNull AppTarget appTarget,
+ @Nullable EventHistory eventHistory,
+ @Nullable ConversationInfo conversationInfo) {
+ mAppTarget = appTarget;
+ mEventHistory = eventHistory;
+ mConversationInfo = conversationInfo;
}
@NonNull
@VisibleForTesting
- ShareShortcutInfo getShareShortcutInfo() {
- return mShareShortcutInfo;
+ AppTarget getAppTarget() {
+ return mAppTarget;
}
@Nullable
@VisibleForTesting
- ConversationData getConversationData() {
- return mConversationData;
+ EventHistory getEventHistory() {
+ return mEventHistory;
+ }
+
+ @Nullable
+ @VisibleForTesting
+ ConversationInfo getConversationInfo() {
+ return mConversationInfo;
}
}
}
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 6190802..fa0febd 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -46,8 +46,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -58,6 +56,9 @@
import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.testing.shadows.ShadowApplicationPackageManager;
import com.android.server.testing.shadows.ShadowUserManager;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -135,7 +136,8 @@
eq(userId)))
.thenReturn(packageInfo);
when(mPackageManagerInternal.getPackage(uid))
- .thenReturn(PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME));
+ .thenReturn(((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME)
+ .hideAsParsed()).hideAsFinal());
}
private PackageInfo buildTestPackageInfo() {
@@ -497,7 +499,9 @@
private void declareCrossProfileAttributeOnCrossProfileApp(boolean value) {
mockCrossProfileAndroidPackage(
- PackageImpl.forParsing(CROSS_PROFILE_APP_PACKAGE_NAME).setCrossProfile(value));
+ ((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME)
+ .setCrossProfile(value)
+ .hideAsParsed()).hideAsFinal());
}
private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 2d5fa23..959dc05 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -50,7 +50,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.parsing.AndroidPackage;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -63,6 +62,7 @@
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 3778e17..e5450a9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -149,7 +149,7 @@
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, false);
mUserBlobs.put(blobHandle2, blobMetadata2);
- mService.addKnownIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
+ mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
blobId1, blobId2);
// Invoke test method
@@ -180,8 +180,10 @@
assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
assertThat(mUserBlobs.get(blobHandle2)).isNull();
- assertThat(mService.getKnownIdsForTest()).containsExactly(
+ assertThat(mService.getActiveIdsForTest()).containsExactly(
sessionId2, sessionId3, blobId1);
+ assertThat(mService.getKnownIdsForTest()).containsExactly(
+ sessionId1, sessionId2, sessionId3, sessionId4, blobId1, blobId2);
}
@Test
@@ -198,12 +200,12 @@
doReturn(String.valueOf(testId3)).when(file3).getName();
doReturn(new File[] {file1, file2, file3}).when(mBlobsDir).listFiles();
- mService.addKnownIdsForTest(testId1, testId3);
+ mService.addActiveIdsForTest(testId1, testId3);
// Invoke test method
mService.handleIdleMaintenanceLocked();
- // Verify unknown blobs are delete
+ // Verify unknown blobs are deleted
verify(file1, never()).delete();
verify(file2).delete();
verify(file3, never()).delete();
@@ -242,7 +244,7 @@
sessionId3, sessionFile3, blobHandle3);
mUserSessions.append(sessionId3, session3);
- mService.addKnownIdsForTest(sessionId1, sessionId2, sessionId3);
+ mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3);
// Invoke test method
mService.handleIdleMaintenanceLocked();
@@ -255,7 +257,9 @@
assertThat(mUserSessions.size()).isEqualTo(1);
assertThat(mUserSessions.get(sessionId2)).isNotNull();
- assertThat(mService.getKnownIdsForTest()).containsExactly(sessionId2);
+ assertThat(mService.getActiveIdsForTest()).containsExactly(sessionId2);
+ assertThat(mService.getKnownIdsForTest()).containsExactly(
+ sessionId1, sessionId2, sessionId3);
}
@Test
@@ -282,7 +286,7 @@
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, false);
mUserBlobs.put(blobHandle3, blobMetadata3);
- mService.addKnownIdsForTest(blobId1, blobId2, blobId3);
+ mService.addActiveIdsForTest(blobId1, blobId2, blobId3);
// Invoke test method
mService.handleIdleMaintenanceLocked();
@@ -295,7 +299,8 @@
assertThat(mUserBlobs.size()).isEqualTo(1);
assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
- assertThat(mService.getKnownIdsForTest()).containsExactly(blobId2);
+ assertThat(mService.getActiveIdsForTest()).containsExactly(blobId2);
+ assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3);
}
private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index d7a3cfd..1bf9c2a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -73,7 +73,9 @@
private static final double DELTA = 0.00001;
private static final String TEST_PACKAGE = "job.test.package";
private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(TEST_PACKAGE, "test");
- private static final Uri TEST_MEDIA_URI = Uri.parse("content://media/path/to/media");
+
+ private static final Uri IMAGES_MEDIA_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ private static final Uri VIDEO_MEDIA_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
@Mock
private JobSchedulerInternal mJobSchedulerInternal;
@@ -127,7 +129,7 @@
@Test
public void testMediaBackupExemption_lateConstraint() {
final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
- .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.setOverrideDeadline(12)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
@@ -138,7 +140,7 @@
@Test
public void testMediaBackupExemption_noConnectivityConstraint() {
final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
- .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.build();
when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
@@ -156,7 +158,7 @@
@Test
public void testMediaBackupExemption_wrongSourcePackage() {
final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
- .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package");
@@ -164,11 +166,12 @@
}
@Test
- public void testMediaBackupExemption_nonMediaUri() {
- final Uri nonMediaUri = Uri.parse("content://not-media/any/path");
+ public void testMediaBackupExemption_nonEligibleUri() {
+ final Uri nonEligibleUri = MediaStore.AUTHORITY_URI.buildUpon()
+ .appendPath("any_path").build();
final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
- .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
- .addTriggerContentUri(new JobInfo.TriggerContentUri(nonMediaUri, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(nonEligibleUri, 0))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
@@ -177,12 +180,25 @@
@Test
public void testMediaBackupExemptionGranted() {
- final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
- .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+ when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+ final JobInfo imageUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build();
- when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
- assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), true);
+ assertEffectiveBucketForMediaExemption(createJobStatus(imageUriJob), true);
+
+ final JobInfo videoUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(VIDEO_MEDIA_URI, 0))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ assertEffectiveBucketForMediaExemption(createJobStatus(videoUriJob), true);
+
+ final JobInfo bothUriJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(IMAGES_MEDIA_URI, 0))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(VIDEO_MEDIA_URI, 0))
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+ .build();
+ assertEffectiveBucketForMediaExemption(createJobStatus(bothUriJob), true);
}
@Test
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index bf2b9be..d148c21 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -93,6 +93,7 @@
},
data: [":JobTestApp"],
+ resource_zips: [":FrameworksServicesTests_apks_as_resources"],
}
java_library {
@@ -115,3 +116,28 @@
"src/com/android/server/pm/SuspendPackagesTest.java",
],
}
+
+// Rules to copy all the test apks to the intermediate raw resource directory
+java_genrule {
+ name: "FrameworksServicesTests_apks_as_resources",
+ srcs: [
+ ":FrameworksCoreTests_install_complete_package_info",
+ ":FrameworksServicesTests_install_intent_filters",
+ ":FrameworksServicesTests_install_split_base",
+ ":FrameworksServicesTests_install_split_feature_a",
+ ":FrameworksServicesTests_install_uses_sdk_0",
+ ":FrameworksServicesTests_install_uses_sdk_q0",
+ ":FrameworksServicesTests_install_uses_sdk_r",
+ ":FrameworksServicesTests_install_uses_sdk_r0",
+ ":FrameworksServicesTests_install_uses_sdk_r5",
+ ],
+ out: ["FrameworkServicesTests_apks_as_resources.res.zip"],
+ tools: ["soong_zip"],
+
+ cmd: "mkdir -p $(genDir)/res/raw && " +
+ "for i in $(in); do " +
+ " x=$${i##*FrameworksCoreTests_}; cp $$i $(genDir)/res/raw/$${x%.apk};" +
+ " x=$${i##*FrameworksServicesTests_}; cp $$i $(genDir)/res/raw/$${x%.apk};" +
+ "done && " +
+ "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
+}
diff --git a/services/tests/servicestests/apks/Android.bp b/services/tests/servicestests/apks/Android.bp
new file mode 100644
index 0000000..3e11604
--- /dev/null
+++ b/services/tests/servicestests/apks/Android.bp
@@ -0,0 +1,7 @@
+java_defaults {
+ name: "FrameworksServicesTests_apks_defaults",
+ sdk_version: "current",
+
+ // Every package should have a native library
+ jni_libs: ["libframeworks_coretests_jni"],
+}
diff --git a/services/tests/servicestests/apks/install-split-base/Android.bp b/services/tests/servicestests/apks/install-split-base/Android.bp
new file mode 100644
index 0000000..1b62aa2
--- /dev/null
+++ b/services/tests/servicestests/apks/install-split-base/Android.bp
@@ -0,0 +1,6 @@
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_split_base",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install-split-base/AndroidManifest.xml b/services/tests/servicestests/apks/install-split-base/AndroidManifest.xml
similarity index 93%
rename from core/tests/coretests/apks/install-split-base/AndroidManifest.xml
rename to services/tests/servicestests/apks/install-split-base/AndroidManifest.xml
index c2bfedd..2979395 100644
--- a/core/tests/coretests/apks/install-split-base/AndroidManifest.xml
+++ b/services/tests/servicestests/apks/install-split-base/AndroidManifest.xml
@@ -15,7 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_split"
+ package="com.android.frameworks.servicestests.install_split"
android:isolatedSplits="true">
<application android:label="ClassloaderSplitApp">
diff --git a/core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java b/services/tests/servicestests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
similarity index 100%
rename from core/tests/coretests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
rename to services/tests/servicestests/apks/install-split-base/src/com/google/android/dexapis/splitapp/BaseActivity.java
diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.bp b/services/tests/servicestests/apks/install-split-feature-a/Android.bp
similarity index 60%
rename from core/tests/coretests/apks/install-split-feature-a/Android.bp
rename to services/tests/servicestests/apks/install-split-feature-a/Android.bp
index 9ec9893..45d8917 100644
--- a/core/tests/coretests/apks/install-split-feature-a/Android.bp
+++ b/services/tests/servicestests/apks/install-split-feature-a/Android.bp
@@ -1,6 +1,6 @@
android_test_helper_app {
- name: "FrameworksCoreTests_install_split_feature_a",
- defaults: ["FrameworksCoreTests_apks_defaults"],
+ name: "FrameworksServicesTests_install_split_feature_a",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
srcs: ["**/*.java"],
diff --git a/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml b/services/tests/servicestests/apks/install-split-feature-a/AndroidManifest.xml
similarity index 93%
rename from core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml
rename to services/tests/servicestests/apks/install-split-feature-a/AndroidManifest.xml
index 3221c75..a3dd55f 100644
--- a/core/tests/coretests/apks/install-split-feature-a/AndroidManifest.xml
+++ b/services/tests/servicestests/apks/install-split-feature-a/AndroidManifest.xml
@@ -15,7 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_split"
+ package="com.android.frameworks.servicestests.install_split"
featureSplit="feature_a">
<application>
diff --git a/core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java b/services/tests/servicestests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
similarity index 100%
rename from core/tests/coretests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
rename to services/tests/servicestests/apks/install-split-feature-a/src/com/google/android/dexapis/splitapp/feature_a/FeatureAActivity.java
diff --git a/services/tests/servicestests/apks/install_intent_filters/Android.bp b/services/tests/servicestests/apks/install_intent_filters/Android.bp
new file mode 100644
index 0000000..59c8524
--- /dev/null
+++ b/services/tests/servicestests/apks/install_intent_filters/Android.bp
@@ -0,0 +1,7 @@
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_intent_filters",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+
+ srcs: ["**/*.java"],
+}
+
diff --git a/core/tests/coretests/apks/install_intent_filters/AndroidManifest.xml b/services/tests/servicestests/apks/install_intent_filters/AndroidManifest.xml
similarity index 87%
rename from core/tests/coretests/apks/install_intent_filters/AndroidManifest.xml
rename to services/tests/servicestests/apks/install_intent_filters/AndroidManifest.xml
index d7ee76e..fc7134d2 100644
--- a/core/tests/coretests/apks/install_intent_filters/AndroidManifest.xml
+++ b/services/tests/servicestests/apks/install_intent_filters/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_intent_filters">
+ package="com.android.frameworks.servicestests.install_intent_filters">
<!--
This manifest declares an activity for testing intent filters.
@@ -24,7 +24,7 @@
-->
<uses-feature
- android:name="com.android.frameworks.coretests.nonexistent" />
+ android:name="com.android.frameworks.servicestests.nonexistent" />
<uses-configuration
android:reqFiveWayNav="false" />
@@ -41,7 +41,7 @@
<application
android:hasCode="true">
<activity
- android:name="com.android.frameworks.coretests.TestActivity">
+ android:name="com.android.frameworks.servicestests.TestActivity">
<intent-filter>
<action android:name="action1"/>
<data android:mimeGroup="mime_group_1"/>
diff --git a/core/tests/coretests/apks/install_intent_filters/src/com/android/frameworks/coretests/TestActivity.java b/services/tests/servicestests/apks/install_intent_filters/src/com/android/frameworks/servicestests/TestActivity.java
similarity index 100%
rename from core/tests/coretests/apks/install_intent_filters/src/com/android/frameworks/coretests/TestActivity.java
rename to services/tests/servicestests/apks/install_intent_filters/src/com/android/frameworks/servicestests/TestActivity.java
diff --git a/services/tests/servicestests/apks/install_uses_sdk/Android.bp b/services/tests/servicestests/apks/install_uses_sdk/Android.bp
new file mode 100644
index 0000000..c24aa2b
--- /dev/null
+++ b/services/tests/servicestests/apks/install_uses_sdk/Android.bp
@@ -0,0 +1,39 @@
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_uses_sdk_r0",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+ manifest: "AndroidManifest-r0.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_uses_sdk_r5",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+ manifest: "AndroidManifest-r5.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_uses_sdk_q0",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+ manifest: "AndroidManifest-q0.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_uses_sdk_r",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+ manifest: "AndroidManifest-r.xml",
+
+ srcs: ["**/*.java"],
+}
+
+android_test_helper_app {
+ name: "FrameworksServicesTests_install_uses_sdk_0",
+ defaults: ["FrameworksServicesTests_apks_defaults"],
+ manifest: "AndroidManifest-0.xml",
+
+ srcs: ["**/*.java"],
+}
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml
similarity index 92%
rename from core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml
index 634228b..215384b 100644
--- a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-0.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-0.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_uses_sdk">
+ package="com.android.frameworks.servicestests.install_uses_sdk">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<!-- This is invalid, because there is no sdk version specified -->
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-q0.xml
similarity index 93%
rename from core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-q0.xml
index 8994966..c0e5867 100644
--- a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-q0.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-q0.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_uses_sdk">
+ package="com.android.frameworks.servicestests.install_uses_sdk">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<!-- This fails because 29 doesn't have an extension sdk -->
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r.xml
similarity index 93%
rename from core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r.xml
index 0d0d8b9..5d22577 100644
--- a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_uses_sdk">
+ package="com.android.frameworks.servicestests.install_uses_sdk">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<!-- This is invalid, because there is no minimum extension version specified -->
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml
similarity index 92%
rename from core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml
index a987afa..c1244f2 100644
--- a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r0.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r0.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_uses_sdk">
+ package="com.android.frameworks.servicestests.install_uses_sdk">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<extension-sdk android:sdkVersion="10000" android:minExtensionVersion="0" />
diff --git a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml
similarity index 93%
rename from core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml
rename to services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml
index 9860096..3410938 100644
--- a/core/tests/coretests/apks/install_uses_sdk/AndroidManifest-r5.xml
+++ b/services/tests/servicestests/apks/install_uses_sdk/AndroidManifest-r5.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.coretests.install_uses_sdk">
+ package="com.android.frameworks.servicestests.install_uses_sdk">
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="29">
<!-- This will fail to install, because minExtensionVersion is not met -->
diff --git a/core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml b/services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml
similarity index 100%
rename from core/tests/coretests/apks/install_uses_sdk/res/values/strings.xml
rename to services/tests/servicestests/apks/install_uses_sdk/res/values/strings.xml
diff --git a/core/tests/coretests/res/raw/com_android_tzdata.apex b/services/tests/servicestests/res/raw/com_android_tzdata.apex
similarity index 100%
rename from core/tests/coretests/res/raw/com_android_tzdata.apex
rename to services/tests/servicestests/res/raw/com_android_tzdata.apex
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 1c8b00f..30bb38a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -111,6 +111,29 @@
any());
}
+ @Test
+ public void testRegisterAuthenticator_callsInitConfiguredStrength() throws Exception {
+
+ final String[] config = {
+ "0:2:15", // ID0:Fingerprint:Strong
+ "1:4:255", // ID1:Iris:Weak
+ "2:8:4095", // ID2:Face:Convenience
+ };
+
+ when(mInjector.getConfiguration(any())).thenReturn(config);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final int fingerprintStrength = 15;
+ final int irisStrength = 255;
+ final int faceStrength = 4095;
+
+ verify(mFingerprintService).initConfiguredStrength(eq(fingerprintStrength));
+ verify(mIrisService).initConfiguredStrength(eq(irisStrength));
+ verify(mFaceService).initConfiguredStrength(eq(faceStrength));
+ }
+
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index dbf2f14..ac818ea 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3952,13 +3952,8 @@
}
public void testIsOrganizationOwnedDevice() throws Exception {
- setupProfileOwner();
// Set up the user manager to return correct user info
- UserInfo managedProfileUserInfo = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
- "managed profile",
- UserInfo.FLAG_MANAGED_PROFILE);
- when(getServices().userManager.getUsers())
- .thenReturn(Arrays.asList(managedProfileUserInfo));
+ addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1);
// Any caller should be able to call this method.
assertFalse(dpm.isOrganizationOwnedDeviceWithManagedProfile());
@@ -5909,8 +5904,6 @@
}
private void configureProfileOwnerOfOrgOwnedDevice(ComponentName who, int userId) {
- when(getServices().userManager.getProfileParent(eq(UserHandle.of(userId))))
- .thenReturn(UserHandle.SYSTEM);
final long ident = mServiceContext.binder.clearCallingIdentity();
mServiceContext.binder.callingUid = UserHandle.getUid(userId, DpmMockContext.SYSTEM_UID);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 37d4081..01f1a3e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -265,6 +265,9 @@
.toArray();
}
);
+ when(userManagerInternal.getUserInfos()).thenReturn(
+ mUserInfos.toArray(new UserInfo[mUserInfos.size()]));
+
when(accountManager.getAccountsAsUser(anyInt())).thenReturn(new Account[0]);
// Create a data directory.
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java
index 0f05212..8dcf21f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SecurityEventTest.java
@@ -1,31 +1,43 @@
package com.android.server.devicepolicy;
import static android.app.admin.SecurityLog.TAG_ADB_SHELL_CMD;
+import static android.app.admin.SecurityLog.TAG_APP_PROCESS_START;
+import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED;
+import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED;
+import static android.app.admin.SecurityLog.TAG_KEY_DESTRUCTION;
+import static android.app.admin.SecurityLog.TAG_KEY_GENERATED;
+import static android.app.admin.SecurityLog.TAG_KEY_IMPORT;
+import static android.app.admin.SecurityLog.TAG_KEY_INTEGRITY_VIOLATION;
+import static android.app.admin.SecurityLog.TAG_MEDIA_MOUNT;
+import static android.app.admin.SecurityLog.TAG_MEDIA_UNMOUNT;
import android.app.admin.SecurityLog.SecurityEvent;
import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.EventLog;
+import android.util.EventLog.Event;
-import java.io.IOException;
+import junit.framework.AssertionFailedError;
+
import java.util.ArrayList;
import java.util.List;
-@SmallTest
public class SecurityEventTest extends DpmTestBase {
- private static long ID = 549;
- private static String DATA = "adb shell some_command";
- public void testSecurityEventId() {
- SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0);
- assertEquals(ID, event.getId());
+ public void testSecurityEventId() throws Exception {
+ SecurityEvent event = createEvent(() -> {
+ EventLog.writeEvent(TAG_ADB_SHELL_CMD, 0);
+ }, TAG_ADB_SHELL_CMD);
event.setId(20);
assertEquals(20, event.getId());
}
- public void testSecurityEventParceling() {
+ public void testSecurityEventParceling() throws Exception {
// GIVEN an event.
- SecurityEvent event = buildSecurityEvents(1 /* generate a single event */, ID).get(0);
+ SecurityEvent event = createEvent(() -> {
+ EventLog.writeEvent(TAG_ADB_SHELL_CMD, "test");
+ }, TAG_ADB_SHELL_CMD);
// WHEN parceling the event.
Parcel p = Parcel.obtain();
p.writeParcelable(event, 0);
@@ -39,23 +51,104 @@
assertEquals(event.getId(), unparceledEvent.getId());
}
- private List<SecurityEvent> buildSecurityEvents(int numEvents, long id) {
- // Write an event to the EventLog.
- for (int i = 0; i < numEvents; i++) {
- EventLog.writeEvent(TAG_ADB_SHELL_CMD, DATA + "_" + i);
+ public void testSecurityEventRedaction() throws Exception {
+ SecurityEvent event;
+
+ // TAG_ADB_SHELL_CMD will has the command redacted
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_ADB_SHELL_CMD, "command");
+ }, TAG_ADB_SHELL_CMD);
+ assertFalse(TextUtils.isEmpty((String) event.getData()));
+
+ // TAG_MEDIA_MOUNT will have the volume label redacted (second data)
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_MEDIA_MOUNT, new Object[] {"path", "label"});
+ }, TAG_MEDIA_MOUNT);
+ assertFalse(TextUtils.isEmpty(event.getStringData(1)));
+ assertTrue(TextUtils.isEmpty(event.redact(0).getStringData(1)));
+
+ // TAG_MEDIA_UNMOUNT will have the volume label redacted (second data)
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_MEDIA_UNMOUNT, new Object[] {"path", "label"});
+ }, TAG_MEDIA_UNMOUNT);
+ assertFalse(TextUtils.isEmpty(event.getStringData(1)));
+ assertTrue(TextUtils.isEmpty(event.redact(0).getStringData(1)));
+
+ // TAG_APP_PROCESS_START will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_APP_PROCESS_START, new Object[] {"process", 12345L,
+ UserHandle.getUid(10, 123), 456, "seinfo", "hash"});
+ }, TAG_APP_PROCESS_START);
+ assertNotNull(event.redact(10));
+ assertNull(event.redact(11));
+
+ // TAG_CERT_AUTHORITY_INSTALLED will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_CERT_AUTHORITY_INSTALLED, new Object[] {1, "subject", 10});
+ }, TAG_CERT_AUTHORITY_INSTALLED);
+ assertNotNull(event.redact(10));
+ assertNull(event.redact(11));
+
+ // TAG_CERT_AUTHORITY_REMOVED will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_CERT_AUTHORITY_REMOVED, new Object[] {1, "subject", 20});
+ }, TAG_CERT_AUTHORITY_REMOVED);
+ assertNotNull(event.redact(20));
+ assertNull(event.redact(0));
+
+ // TAG_KEY_GENERATED will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_KEY_GENERATED,
+ new Object[] {1, "alias", UserHandle.getUid(0, 123)});
+ }, TAG_KEY_GENERATED);
+ assertNotNull(event.redact(0));
+ assertNull(event.redact(10));
+
+ // TAG_KEY_IMPORT will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_KEY_IMPORT,
+ new Object[] {1, "alias", UserHandle.getUid(1, 123)});
+ }, TAG_KEY_IMPORT);
+ assertNotNull(event.redact(1));
+ assertNull(event.redact(10));
+
+ // TAG_KEY_DESTRUCTION will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_KEY_DESTRUCTION,
+ new Object[] {1, "alias", UserHandle.getUid(2, 123)});
+ }, TAG_KEY_DESTRUCTION);
+ assertNotNull(event.redact(2));
+ assertNull(event.redact(10));
+
+ // TAG_KEY_INTEGRITY_VIOLATION will be fully redacted if user does not match
+ event = createEvent(() -> {
+ EventLog.writeEvent(TAG_KEY_INTEGRITY_VIOLATION,
+ new Object[] {"alias", UserHandle.getUid(2, 123)});
+ }, TAG_KEY_INTEGRITY_VIOLATION);
+ assertNotNull(event.redact(2));
+ assertNull(event.redact(10));
+
+ }
+
+ /**
+ * Creates an Event object. Only the native code has the serialization and deserialization logic
+ * so need to actually emit a real log in order to generate the object.
+ */
+ private SecurityEvent createEvent(Runnable generator, int expectedTag) throws Exception {
+ Long markerData = System.currentTimeMillis();
+ EventLog.writeEvent(expectedTag, markerData);
+ generator.run();
+
+ List<Event> events = new ArrayList<>();
+ // Give the message some time to show up in the log
+ Thread.sleep(20);
+ EventLog.readEvents(new int[] {expectedTag}, events);
+
+ for (int i = 0; i < events.size() - 1; i++) {
+ if (markerData.equals(events.get(i).getData())) {
+ return new SecurityEvent(0, events.get(i + 1).getBytes());
+ }
}
- List<EventLog.Event> events = new ArrayList<>();
- try {
- EventLog.readEvents(new int[]{TAG_ADB_SHELL_CMD}, events);
- } catch (IOException e) {
- fail("Reading a test event from storage failed: " + e);
- }
- assertTrue("Unexpected number of events read from the log.", events.size() >= numEvents);
- // Read events generated by test, from the end of the log.
- List<SecurityEvent> securityEvents = new ArrayList<>();
- for (int i = events.size() - numEvents; i < events.size(); i++) {
- securityEvents.add(new SecurityEvent(id++, events.get(i).getBytes()));
- }
- return securityEvents;
+ throw new AssertionFailedError("Unable to locate marker event");
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 25d0778..feae1e1 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -29,6 +29,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
+import com.android.server.display.DisplayModeDirector.RefreshRateRange;
+import com.android.server.display.DisplayModeDirector.Vote;
+
+import com.google.common.truth.Truth;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,6 +43,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DisplayModeDirectorTest {
+ // The tolerance within which we consider something approximately equals.
+ private static final float FLOAT_TOLERANCE = 0.01f;
+
private Context mContext;
@Before
@@ -56,30 +65,22 @@
modes[i - minFps] = new Display.Mode(
/*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i);
}
- SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>();
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
supportedModesByDisplay.put(displayId, modes);
director.injectSupportedModesByDisplay(supportedModesByDisplay);
- SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>();
+ SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<>();
defaultModesByDisplay.put(displayId, modes[0]);
director.injectDefaultModeByDisplay(defaultModesByDisplay);
return director;
}
- private int[] intRange(int min, int max) {
- int[] range = new int[max - min + 1];
- for (int i = min; i <= max; i++) {
- range[i - min] = i;
- }
- return range;
- }
-
@Test
public void testDisplayModeVoting() {
int displayId = 0;
// With no votes present, DisplayModeDirector should allow any refresh rate.
- assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/60,
- new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY)),
+ assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/60,
+ new RefreshRateRange(0f, Float.POSITIVE_INFINITY)),
createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs(
displayId));
@@ -93,20 +94,16 @@
int maxFps = 90;
DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
assertTrue(2 * numPriorities < maxFps - minFps + 1);
- SparseArray<DisplayModeDirector.Vote> votes =
- new SparseArray<DisplayModeDirector.Vote>();
- SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
- new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
for (int i = 0; i < numPriorities; i++) {
- int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i;
- votes.put(
- priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i));
+ int priority = Vote.MIN_PRIORITY + i;
+ votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i));
director.injectVotesByDisplay(votesByDisplay);
- assertEquals(
- new DisplayModeDirector.DesiredDisplayModeSpecs(
+ assertEquals(new DesiredDisplayModeSpecs(
/*baseModeId=*/minFps + i,
- new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i)),
+ new RefreshRateRange(minFps + i, maxFps - i)),
director.getDesiredDisplayModeSpecs(displayId));
}
}
@@ -116,19 +113,35 @@
{
assertTrue(numPriorities >= 2);
DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
- SparseArray<DisplayModeDirector.Vote> votes =
- new SparseArray<DisplayModeDirector.Vote>();
- SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
- new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(displayId, votes);
- votes.put(DisplayModeDirector.Vote.MAX_PRIORITY,
- DisplayModeDirector.Vote.forRefreshRates(65, 85));
- votes.put(DisplayModeDirector.Vote.MIN_PRIORITY,
- DisplayModeDirector.Vote.forRefreshRates(70, 80));
+ votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85));
+ votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80));
director.injectVotesByDisplay(votesByDisplay);
- assertEquals(new DisplayModeDirector.DesiredDisplayModeSpecs(/*baseModeId=*/70,
- new DisplayModeDirector.RefreshRateRange(70, 80)),
+ assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/70,
+ new RefreshRateRange(70, 80)),
director.getDesiredDisplayModeSpecs(displayId));
}
}
+
+ @Test
+ public void testVotingWithFloatingPointErrors() {
+ int displayId = 0;
+ DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(displayId, votes);
+ float error = FLOAT_TOLERANCE / 4;
+ votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(0, 60));
+ votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forRefreshRates(60 + error, 60 + error));
+ votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE,
+ Vote.forRefreshRates(60 - error, 60 - error));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+
+ Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+ Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index 3dc26af..ab21ab0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -73,7 +73,7 @@
private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
- private static final int INVALID_KEY_VALUE = 6;
+ private static final int INVALID_KEY_VALUE = 8;
private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
diff --git a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java
index 6fafe11..9b076e8 100644
--- a/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/MockableLocationProviderTest.java
@@ -118,16 +118,6 @@
}
@Test
- public void testRequestSetAllowed() {
- mProvider.requestSetAllowed(true);
- verify(mRealProvider, times(1)).onRequestSetAllowed(true);
-
- mProvider.setMockProvider(mMockProvider);
- mProvider.requestSetAllowed(true);
- verify(mMockProvider, times(1)).onRequestSetAllowed(true);
- }
-
- @Test
public void testSendExtraCommand() {
mProvider.sendExtraCommand(0, 0, "command", null);
verify(mRealProvider, times(1)).onExtraCommand(0, 0, "command", null);
diff --git a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java
index 762080f..5943f67 100644
--- a/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/servicestests/src/com/android/server/location/test/FakeProvider.java
@@ -38,8 +38,5 @@
protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
@Override
- protected void onRequestSetAllowed(boolean allowed) {}
-
- @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
index ef12948..063cd5da 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
@@ -16,8 +16,8 @@
package com.android.server.om
-import android.content.pm.parsing.AndroidPackage
import android.net.Uri
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -196,11 +196,13 @@
whenever(packageName) { "$TARGET_PACKAGE_NAME$increment" }
whenever(overlayables) { mapOf("overlayableName$increment" to ACTOR_NAME) }
whenever(toString()) { "Package{$packageName}" }
+ whenever(isOverlay) { false }
}
private fun mockOverlay(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
whenever(packageName) { "$OVERLAY_PACKAGE_NAME$increment" }
whenever(overlayables) { emptyMap<String, String>() }
whenever(toString()) { "Package{$packageName}" }
+ whenever(isOverlay) { true }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java
index b614a4f..443718d 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/AggregateEventHistoryImplTest.java
@@ -21,11 +21,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.File;
import java.util.List;
@RunWith(JUnit4.class)
@@ -60,11 +65,16 @@
EventHistoryImpl.Injector injector = new EventHistoryImplInjector();
- mEventHistory1 = new EventHistoryImpl(injector);
+ Context ctx = InstrumentationRegistry.getContext();
+ File testDir = new File(ctx.getCacheDir(), "testdir");
+ MockScheduledExecutorService mockScheduledExecutorService =
+ new MockScheduledExecutorService();
+
+ mEventHistory1 = new EventHistoryImpl(injector, testDir, mockScheduledExecutorService);
mEventHistory1.addEvent(E1);
mEventHistory1.addEvent(E2);
- mEventHistory2 = new EventHistoryImpl(injector);
+ mEventHistory2 = new EventHistoryImpl(injector, testDir, mockScheduledExecutorService);
mEventHistory2.addEvent(E3);
mEventHistory2.addEvent(E4);
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index 03b5e38..d138700 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ShortcutInfo;
@@ -63,7 +62,6 @@
private static final String PHONE_NUMBER_3 = "+9234567890";
private MockScheduledExecutorService mMockScheduledExecutorService;
- private TestContactQueryHelper mTestContactQueryHelper;
private ConversationStore mConversationStore;
private File mFile;
@@ -71,7 +69,6 @@
public void setUp() {
Context ctx = InstrumentationRegistry.getContext();
mFile = new File(ctx.getCacheDir(), "testdir");
- mTestContactQueryHelper = new TestContactQueryHelper(ctx);
resetConversationStore();
}
@@ -207,9 +204,6 @@
mConversationStore.deleteConversation(SHORTCUT_ID_3);
mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS);
- mTestContactQueryHelper.setQueryResult(true, true);
- mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2);
-
resetConversationStore();
ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
@@ -240,9 +234,6 @@
mConversationStore.addOrUpdate(in2);
mMockScheduledExecutorService.fastForwardTime(DateUtils.MINUTE_IN_MILLIS);
- mTestContactQueryHelper.setQueryResult(true);
- mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER);
-
resetConversationStore();
ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
@@ -256,10 +247,6 @@
mConversationStore.addOrUpdate(in3);
mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS);
- mTestContactQueryHelper.reset();
- mTestContactQueryHelper.setQueryResult(true, true, true);
- mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2, PHONE_NUMBER_3);
-
resetConversationStore();
out1 = mConversationStore.getConversation(SHORTCUT_ID);
out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
@@ -290,9 +277,6 @@
// loadConversationFromDisk gets called each time we call #resetConversationStore().
assertEquals(2, mMockScheduledExecutorService.getExecutes().size());
- mTestContactQueryHelper.setQueryResult(true, true);
- mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2);
-
resetConversationStore();
ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
@@ -303,8 +287,7 @@
private void resetConversationStore() {
mFile.mkdir();
mMockScheduledExecutorService = new MockScheduledExecutorService();
- mConversationStore = new ConversationStore(mFile, mMockScheduledExecutorService,
- mTestContactQueryHelper);
+ mConversationStore = new ConversationStore(mFile, mMockScheduledExecutorService);
mConversationStore.loadConversationsFromDisk();
}
@@ -326,54 +309,4 @@
.setBubbled(true)
.build();
}
-
- private static class TestContactQueryHelper extends ContactsQueryHelper {
-
- private int mQueryCalls;
- private boolean[] mQueryResult;
-
- private int mPhoneNumberCalls;
- private String[] mPhoneNumberResult;
-
- TestContactQueryHelper(Context context) {
- super(context);
-
- mQueryCalls = 0;
- mPhoneNumberCalls = 0;
- }
-
- private void setQueryResult(boolean... values) {
- mQueryResult = values;
- }
-
- private void setPhoneNumberResult(String... values) {
- mPhoneNumberResult = values;
- }
-
- private void reset() {
- mQueryCalls = 0;
- mQueryResult = null;
- mPhoneNumberCalls = 0;
- mPhoneNumberResult = null;
- }
-
- @Override
- boolean query(String contactUri) {
- if (mQueryResult != null && mQueryCalls < mQueryResult.length) {
- return mQueryResult[mQueryCalls++];
- }
- mQueryCalls++;
- return false;
- }
-
- @Override
- @Nullable
- String getPhoneNumber() {
- if (mPhoneNumberResult != null && mPhoneNumberCalls < mPhoneNumberResult.length) {
- return mPhoneNumberResult[mPhoneNumberCalls++];
- }
- mPhoneNumberCalls++;
- return null;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 5e104a5..f0b7d20 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -52,11 +52,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.LauncherApps.ShortcutChangeCallback;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.AndroidPackage;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.CancellationSignal;
@@ -73,12 +74,16 @@
import com.android.internal.app.ChooserActivity;
import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -87,6 +92,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -100,7 +106,9 @@
private static final int USER_ID_PRIMARY_MANAGED = 10;
private static final int USER_ID_SECONDARY = 11;
private static final String TEST_PKG_NAME = "pkg";
+ private static final String TEST_CLASS_NAME = "class";
private static final String TEST_SHORTCUT_ID = "sc";
+ private static final int TEST_PKG_UID = 35;
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
private static final String NOTIFICATION_CHANNEL_ID = "test : sc";
@@ -110,7 +118,9 @@
@Mock private ShortcutServiceInternal mShortcutServiceInternal;
@Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock private PackageManagerInternal mPackageManagerInternal;
+ @Mock private NotificationManagerInternal mNotificationManagerInternal;
@Mock private UserManager mUserManager;
+ @Mock private PackageManager mPackageManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private TelecomManager mTelecomManager;
@Mock private ContentResolver mContentResolver;
@@ -120,13 +130,16 @@
@Mock private StatusBarNotification mStatusBarNotification;
@Mock private Notification mNotification;
+ @Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
+
private NotificationChannel mNotificationChannel;
private DataManager mDataManager;
private CancellationSignal mCancellationSignal;
+ private ShortcutChangeCallback mShortcutChangeCallback;
private TestInjector mInjector;
@Before
- public void setUp() {
+ public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
addLocalServiceMock(ShortcutServiceInternal.class, mShortcutServiceInternal);
@@ -142,8 +155,12 @@
return null;
}).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
+ addLocalServiceMock(NotificationManagerInternal.class, mNotificationManagerInternal);
+
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
when(mContext.getPackageName()).thenReturn("android");
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
Context originalContext = getInstrumentation().getTargetContext();
when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
@@ -174,7 +191,8 @@
when(mUserManager.getEnabledProfiles(USER_ID_SECONDARY))
.thenReturn(Collections.singletonList(buildUserInfo(USER_ID_SECONDARY)));
- when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ when(mPackageManager.getPackageUidAsUser(TEST_PKG_NAME, USER_ID_PRIMARY))
+ .thenReturn(TEST_PKG_UID);
when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
when(mStatusBarNotification.getPackageName()).thenReturn(TEST_PKG_NAME);
@@ -191,6 +209,10 @@
mInjector = new TestInjector();
mDataManager = new DataManager(mContext, mInjector);
mDataManager.initialize();
+
+ verify(mShortcutServiceInternal).addShortcutChangeCallback(
+ mShortcutChangeCallbackCaptor.capture());
+ mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
}
@After
@@ -206,13 +228,13 @@
mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1",
buildPerson(true, false)));
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2",
buildPerson(false, true)));
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -236,9 +258,9 @@
@Test
public void testAccessConversationForUnlockedUsersOnly() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson()));
- mDataManager.onShortcutAddedOrUpdated(
+ mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
List<ConversationInfo> conversations = getConversationsInPrimary();
@@ -261,11 +283,12 @@
}
@Test
- public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
+ public void testReportAppTargetEvent_directSharing()
+ throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
.build();
@@ -274,7 +297,55 @@
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
- mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+
+ List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+ Event.SHARE_EVENT_TYPES);
+ assertEquals(1, activeShareTimeSlots.size());
+ }
+
+ @Test
+ public void testReportAppTargetEvent_directSharing_createConversation()
+ throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ null);
+ AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
+
+ List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
+ Event.SHARE_EVENT_TYPES);
+ assertEquals(1, activeShareTimeSlots.size());
+ ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+ .getConversationStore()
+ .getConversation(TEST_SHORTCUT_ID);
+ assertNotNull(conversationInfo);
+ assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
+ }
+
+ @Test
+ public void testReportAppTargetEvent_appSharing()
+ throws IntentFilter.MalformedMimeTypeException {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ AppTarget appTarget = new AppTarget.Builder(
+ new AppTargetId(TEST_SHORTCUT_ID),
+ TEST_PKG_NAME,
+ UserHandle.of(USER_ID_PRIMARY))
+ .setClassName(TEST_CLASS_NAME)
+ .build();
+ AppTargetEvent appTargetEvent =
+ new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
+ .build();
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
+
+ mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
Event.SHARE_EVENT_TYPES);
@@ -288,7 +359,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
final String newPhoneNumber = "+1000000000";
mInjector.mContactsQueryHelper.mIsStarred = true;
@@ -312,7 +383,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -330,7 +401,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -350,7 +421,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -375,7 +446,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -401,7 +472,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
@@ -424,13 +495,50 @@
}
@Test
+ public void testShortcutAddedOrUpdated() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
+ Collections.singletonList(shortcut), UserHandle.of(USER_ID_PRIMARY));
+
+ List<ConversationInfo> conversations = getConversationsInPrimary();
+
+ assertEquals(1, conversations.size());
+ assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
+ }
+
+ @Test
+ public void testShortcutDeleted() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc1",
+ buildPerson());
+ ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc2",
+ buildPerson());
+ mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
+ Arrays.asList(shortcut1, shortcut2), UserHandle.of(USER_ID_PRIMARY));
+ mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
+ Collections.singletonList(shortcut1), UserHandle.of(USER_ID_PRIMARY));
+
+ List<ConversationInfo> conversations = getConversationsInPrimary();
+
+ assertEquals(1, conversations.size());
+ assertEquals("sc2", conversations.get(0).getShortcutId());
+
+ verify(mNotificationManagerInternal)
+ .onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, "sc1");
+ }
+
+ @Test
public void testCallLogContentObserver() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
ContentObserver contentObserver = mDataManager.getCallLogContentObserverForTesting();
contentObserver.onChange(false);
@@ -453,7 +561,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
ContentObserver contentObserver = mDataManager.getMmsSmsContentObserverForTesting();
@@ -476,7 +584,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
@@ -493,7 +601,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
doAnswer(ans -> null).when(mPackageManagerInternal)
@@ -508,7 +616,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
long currentTimestamp = System.currentTimeMillis();
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
@@ -529,7 +637,7 @@
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
- mDataManager.onShortcutAddedOrUpdated(shortcut);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
long currentTimestamp = System.currentTimeMillis();
@@ -715,6 +823,11 @@
}
@Override
+ Executor getBackgroundExecutor() {
+ return Runnable::run;
+ }
+
+ @Override
ContactsQueryHelper createContactsQueryHelper(Context context) {
return mContactsQueryHelper;
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java b/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java
index 43e1001..825ca10 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/EventHistoryImplTest.java
@@ -21,18 +21,27 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
+import android.os.FileUtils;
+import android.text.format.DateUtils;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.File;
import java.util.List;
+import java.util.Map;
@RunWith(JUnit4.class)
public final class EventHistoryImplTest {
-
private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50");
private static final Event E1 = new Event(timestamp("01-06 05:26"),
@@ -41,26 +50,48 @@
Event.TYPE_NOTIFICATION_OPENED);
private static final Event E3 = new Event(timestamp("01-30 03:06"),
Event.TYPE_SHARE_IMAGE);
- private static final Event E4 = new Event(timestamp("01-30 18:14"),
+ private static final Event E4 = new Event(timestamp("01-30 16:14"),
+ Event.TYPE_SMS_INCOMING);
+ private static final Event E5 = new Event(timestamp("01-30 18:30"),
Event.TYPE_SMS_INCOMING);
+ private static final EventIndex.Injector EVENT_INDEX_INJECTOR = new EventIndex.Injector() {
+ @Override
+ long currentTimeMillis() {
+ return CURRENT_TIMESTAMP;
+ }
+ };
+ private static final EventHistoryImpl.Injector EVENT_HISTORY_INJECTOR =
+ new EventHistoryImpl.Injector() {
+ @Override
+ EventIndex createEventIndex() {
+ return new EventIndex(EVENT_INDEX_INJECTOR);
+ }
+
+ @Override
+ long currentTimeMillis() {
+ return CURRENT_TIMESTAMP;
+ }
+ };
+
private EventHistoryImpl mEventHistory;
+ private File mCacheDir;
+ private File mFile;
+ private MockScheduledExecutorService mMockScheduledExecutorService;
@Before
public void setUp() {
- EventIndex.Injector eventIndexInjector = new EventIndex.Injector() {
- @Override
- long currentTimeMillis() {
- return CURRENT_TIMESTAMP;
- }
- };
- EventHistoryImpl.Injector eventHistoryInjector = new EventHistoryImpl.Injector() {
- @Override
- EventIndex createEventIndex() {
- return new EventIndex(eventIndexInjector);
- }
- };
- mEventHistory = new EventHistoryImpl(eventHistoryInjector);
+ Context ctx = InstrumentationRegistry.getContext();
+ mCacheDir = ctx.getCacheDir();
+ mFile = new File(mCacheDir, "testdir");
+ mMockScheduledExecutorService = new MockScheduledExecutorService();
+ mEventHistory = new EventHistoryImpl(EVENT_HISTORY_INJECTOR, mFile,
+ mMockScheduledExecutorService);
+ }
+
+ @After
+ public void tearDown() {
+ FileUtils.deleteContentsAndDir(mFile);
}
@Test
@@ -115,4 +146,105 @@
Sets.newArraySet(Event.TYPE_SHARE_IMAGE), 0L, Long.MAX_VALUE);
assertEquals(1, events.size());
}
+
+ @Test
+ public void testPersistenceAndRestoration() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+ mEventHistory.addEvent(E5);
+
+ // futures of events and event index flush.
+ long futuresExecuted = mMockScheduledExecutorService.fastForwardTime(
+ 3L * DateUtils.MINUTE_IN_MILLIS);
+ assertEquals(2, futuresExecuted);
+
+ EventIndex indexBeforePowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+
+ resetAndLoadEventHistory();
+
+ List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(2, events.size());
+ assertTrue(events.containsAll(Lists.newArrayList(E4, E5)));
+
+ EventIndex indexAfterPowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+ assertEquals(indexBeforePowerOff, indexAfterPowerOff);
+ }
+
+ @Test
+ public void testMimicDevicePowerOff() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+ mEventHistory.addEvent(E5);
+ mEventHistory.saveToDisk();
+
+ EventIndex indexBeforePowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+
+ // Ensure that futures were cancelled and the immediate flush occurred.
+ assertEquals(0, mMockScheduledExecutorService.getFutures().size());
+
+ // Expect to see 2 executes from #saveToDisk, one for events and another for index.
+ assertEquals(2, mMockScheduledExecutorService.getExecutes().size());
+
+ resetAndLoadEventHistory();
+
+ List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertEquals(2, events.size());
+ assertTrue(events.containsAll(Lists.newArrayList(E4, E5)));
+
+ EventIndex indexAfterPowerOff = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+ assertEquals(indexBeforePowerOff, indexAfterPowerOff);
+ }
+
+ @Test
+ public void testOnDestroy() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+ mEventHistory.addEvent(E5);
+ mEventHistory.saveToDisk();
+
+ mEventHistory.onDestroy();
+
+ List<Event> events = mEventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+ assertTrue(events.isEmpty());
+
+ EventIndex index = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+ assertTrue(index.isEmpty());
+ }
+
+ @Test
+ public void testEventHistoriesImplFromDisk() {
+ mEventHistory.addEvent(E1);
+ mEventHistory.addEvent(E2);
+ mEventHistory.addEvent(E3);
+ mEventHistory.addEvent(E4);
+ mEventHistory.addEvent(E5);
+ mEventHistory.saveToDisk();
+
+ EventIndex indexBefore = mEventHistory.getEventIndex(Event.ALL_EVENT_TYPES);
+
+ Map<String, EventHistoryImpl> map = EventHistoryImpl.eventHistoriesImplFromDisk(
+ EVENT_HISTORY_INJECTOR, mCacheDir, mMockScheduledExecutorService);
+ assertEquals(1, map.size());
+ assertTrue(map.containsKey("testdir"));
+
+ List<Event> events = map.get("testdir").queryEvents(Event.ALL_EVENT_TYPES, 0L,
+ Long.MAX_VALUE);
+ assertEquals(2, events.size());
+ assertTrue(events.containsAll(Lists.newArrayList(E4, E5)));
+
+ EventIndex indexAfter = map.get("testdir").getEventIndex(Event.ALL_EVENT_TYPES);
+ assertEquals(indexBefore, indexAfter);
+ }
+
+ private void resetAndLoadEventHistory() {
+ mEventHistory = new EventHistoryImpl(EVENT_HISTORY_INJECTOR, mFile,
+ mMockScheduledExecutorService);
+ mEventHistory.loadFromDisk();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
index 8b8ba12..aecbc8d 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
@@ -54,8 +54,8 @@
long totalExecuted = 0;
for (MockScheduledFuture<?> future : futuresCopy) {
if (future.getDelay() < mTimeElapsedMillis) {
- future.getCommand().run();
- mExecutes.add(future.getCommand());
+ future.getRunnable().run();
+ mExecutes.add(future.getRunnable());
totalExecuted += 1;
} else {
mFutures.add(future);
@@ -96,7 +96,8 @@
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
TimeUnit unit) {
- throw new UnsupportedOperationException();
+ Preconditions.checkState(unit == TimeUnit.MILLISECONDS);
+ return new MockScheduledFuture<>(command, period, unit);
}
@Override
@@ -132,7 +133,13 @@
@Override
public <T> Future<T> submit(Callable<T> task) {
- throw new UnsupportedOperationException();
+ MockScheduledFuture<T> future = new MockScheduledFuture<>(task, 0, TimeUnit.MILLISECONDS);
+ try {
+ future.getCallable().call();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return future;
}
@Override
@@ -141,11 +148,11 @@
}
@Override
- public Future<?> submit(Runnable command) {
- mExecutes.add(command);
- MockScheduledFuture<?> future = new MockScheduledFuture<>(command, 0,
+ public Future<?> submit(Runnable runnable) {
+ mExecutes.add(runnable);
+ MockScheduledFuture<?> future = new MockScheduledFuture<>(runnable, 0,
TimeUnit.MILLISECONDS);
- future.getCommand().run();
+ future.getRunnable().run();
return future;
}
@@ -181,12 +188,22 @@
class MockScheduledFuture<V> implements ScheduledFuture<V> {
- private final Runnable mCommand;
+ private final Runnable mRunnable;
+ private final Callable<V> mCallable;
private final long mDelay;
private boolean mCancelled = false;
- MockScheduledFuture(Runnable command, long delay, TimeUnit timeUnit) {
- mCommand = command;
+ MockScheduledFuture(Runnable runnable, long delay, TimeUnit timeUnit) {
+ this(runnable, null, delay);
+ }
+
+ MockScheduledFuture(Callable<V> callable, long delay, TimeUnit timeUnit) {
+ this(null, callable, delay);
+ }
+
+ private MockScheduledFuture(Runnable runnable, Callable<V> callable, long delay) {
+ mCallable = callable;
+ mRunnable = runnable;
mDelay = delay;
}
@@ -194,8 +211,12 @@
return mDelay;
}
- public Runnable getCommand() {
- return mCommand;
+ public Runnable getRunnable() {
+ return mRunnable;
+ }
+
+ public Callable<V> getCallable() {
+ return mCallable;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
index e52cdf5..8191d17 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
@@ -61,7 +61,7 @@
testDir.mkdir();
mPackageData = new PackageData(
PACKAGE_NAME, USER_ID, pkg -> mIsDefaultDialer, pkg -> mIsDefaultSmsApp,
- new MockScheduledExecutorService(), testDir, new ContactsQueryHelper(ctx));
+ new MockScheduledExecutorService(), testDir);
ConversationInfo conversationInfo = new ConversationInfo.Builder()
.setShortcutId(SHORTCUT_ID)
.setLocusId(LOCUS_ID)
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index d444466..7934d33 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -16,6 +16,8 @@
package com.android.server.people.data;
+import static com.android.server.people.data.TestUtils.timestamp;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -62,7 +64,8 @@
private static final LocusId LOCUS_ID_1 = new LocusId("locus_1");
private static final LocusId LOCUS_ID_2 = new LocusId("locus_2");
- @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
private TestPackageData mPackageData;
private UsageStatsQueryHelper mHelper;
@@ -76,10 +79,9 @@
Context ctx = InstrumentationRegistry.getContext();
File testDir = new File(ctx.getCacheDir(), "testdir");
ScheduledExecutorService scheduledExecutorService = new MockScheduledExecutorService();
- ContactsQueryHelper helper = new ContactsQueryHelper(ctx);
mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false,
- scheduledExecutorService, testDir, helper);
+ scheduledExecutorService, testDir);
mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
.setShortcutId(SHORTCUT_ID)
.setLocusId(LOCUS_ID_1)
@@ -218,9 +220,8 @@
private ConversationInfo mConversationInfo;
TestConversationStore(File packageDir,
- ScheduledExecutorService scheduledExecutorService,
- ContactsQueryHelper helper) {
- super(packageDir, scheduledExecutorService, helper);
+ ScheduledExecutorService scheduledExecutorService) {
+ super(packageDir, scheduledExecutorService);
}
@Override
@@ -233,17 +234,16 @@
private static class TestPackageData extends PackageData {
private final TestConversationStore mConversationStore;
- private final TestEventStore mEventStore = new TestEventStore();
+ private final TestEventStore mEventStore;
TestPackageData(@NonNull String packageName, @UserIdInt int userId,
@NonNull Predicate<String> isDefaultDialerPredicate,
@NonNull Predicate<String> isDefaultSmsAppPredicate,
- @NonNull ScheduledExecutorService scheduledExecutorService, @NonNull File rootDir,
- @NonNull ContactsQueryHelper helper) {
+ @NonNull ScheduledExecutorService scheduledExecutorService, @NonNull File rootDir) {
super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate,
- scheduledExecutorService, rootDir, helper);
- mConversationStore = new TestConversationStore(rootDir, scheduledExecutorService,
- helper);
+ scheduledExecutorService, rootDir);
+ mConversationStore = new TestConversationStore(rootDir, scheduledExecutorService);
+ mEventStore = new TestEventStore(rootDir, scheduledExecutorService);
}
@Override
@@ -261,8 +261,31 @@
private static class TestEventStore extends EventStore {
- private final EventHistoryImpl mShortcutEventHistory = new TestEventHistoryImpl();
- private final EventHistoryImpl mLocusEventHistory = new TestEventHistoryImpl();
+ private static final long CURRENT_TIMESTAMP = timestamp("01-30 18:50");
+ private static final EventIndex.Injector EVENT_INDEX_INJECTOR = new EventIndex.Injector() {
+ @Override
+ long currentTimeMillis() {
+ return CURRENT_TIMESTAMP;
+ }
+ };
+ private static final EventHistoryImpl.Injector EVENT_HISTORY_INJECTOR =
+ new EventHistoryImpl.Injector() {
+ @Override
+ EventIndex createEventIndex() {
+ return new EventIndex(EVENT_INDEX_INJECTOR);
+ }
+ };
+
+ private final EventHistoryImpl mShortcutEventHistory;
+ private final EventHistoryImpl mLocusEventHistory;
+
+ TestEventStore(File rootDir, ScheduledExecutorService scheduledExecutorService) {
+ super(rootDir, scheduledExecutorService);
+ mShortcutEventHistory = new TestEventHistoryImpl(EVENT_HISTORY_INJECTOR, rootDir,
+ scheduledExecutorService);
+ mLocusEventHistory = new TestEventHistoryImpl(EVENT_HISTORY_INJECTOR, rootDir,
+ scheduledExecutorService);
+ }
@Override
@NonNull
@@ -280,6 +303,11 @@
private final List<Event> mEvents = new ArrayList<>();
+ TestEventHistoryImpl(Injector injector, File rootDir,
+ ScheduledExecutorService scheduledExecutorService) {
+ super(injector, rootDir, scheduledExecutorService);
+ }
+
@Override
@NonNull
public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) {
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index f498a94..c6cd347 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -16,16 +16,19 @@
package com.android.server.people.prediction;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -33,22 +36,26 @@
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import android.util.Range;
import com.android.server.people.data.ConversationInfo;
import com.android.server.people.data.DataManager;
import com.android.server.people.data.EventHistory;
+import com.android.server.people.data.EventIndex;
import com.android.server.people.data.PackageData;
-import com.android.server.people.prediction.ShareTargetPredictor.ShareTarget;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
@RunWith(JUnit4.class)
public final class ShareTargetPredictorTest {
@@ -57,17 +64,32 @@
private static final int NUM_PREDICTED_TARGETS = 5;
private static final int USER_ID = 0;
private static final String PACKAGE_1 = "pkg1";
- private static final String CLASS_1 = "cls1";
private static final String PACKAGE_2 = "pkg2";
+ private static final String PACKAGE_3 = "pkg3";
+ private static final String CLASS_1 = "cls1";
private static final String CLASS_2 = "cls2";
@Mock private Context mContext;
@Mock private DataManager mDataManager;
+ @Mock private Consumer<List<AppTarget>> mUpdatePredictionsMethod;
@Mock private PackageData mPackageData1;
@Mock private PackageData mPackageData2;
+ @Mock private EventHistory mEventHistory1;
+ @Mock private EventHistory mEventHistory2;
+ @Mock private EventHistory mEventHistory3;
+ @Mock private EventHistory mEventHistory4;
+ @Mock private EventHistory mEventHistory5;
+ @Mock private EventHistory mEventHistory6;
+
+ @Mock private EventIndex mEventIndex1;
+ @Mock private EventIndex mEventIndex2;
+ @Mock private EventIndex mEventIndex3;
+ @Mock private EventIndex mEventIndex4;
+ @Mock private EventIndex mEventIndex5;
+ @Mock private EventIndex mEventIndex6;
+ @Captor private ArgumentCaptor<List<AppTarget>> mAppTargetCaptor;
private List<ShareShortcutInfo> mShareShortcuts = new ArrayList<>();
-
private ShareTargetPredictor mPredictor;
@Before
@@ -84,11 +106,11 @@
.setExtras(new Bundle())
.build();
mPredictor = new ShareTargetPredictor(
- predictionContext, targets -> { }, mDataManager, USER_ID);
+ predictionContext, mUpdatePredictionsMethod, mDataManager, USER_ID);
}
@Test
- public void testGetShareTargets() {
+ public void testPredictTargets() {
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
@@ -99,24 +121,148 @@
when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
// "sc4" does not have a ConversationInfo.
- when(mPackageData1.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
- when(mPackageData2.getEventHistory(anyString())).thenReturn(mock(EventHistory.class));
+ when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1);
+ when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2);
+ when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3);
+ when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+ when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+ when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
- List<ShareTarget> shareTargets = mPredictor.getShareTargets();
+ mPredictor.predictTargets();
- assertEquals(4, shareTargets.size());
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ List<AppTarget> res = mAppTargetCaptor.getValue();
+ assertEquals(4, res.size());
- assertEquals("sc1", shareTargets.get(0).getShareShortcutInfo().getShortcutInfo().getId());
- assertNotNull(shareTargets.get(0).getConversationData());
+ assertEquals("sc3", res.get(0).getId().getId());
+ assertEquals(CLASS_2, res.get(0).getClassName());
+ assertEquals(PACKAGE_2, res.get(0).getPackageName());
- assertEquals("sc2", shareTargets.get(1).getShareShortcutInfo().getShortcutInfo().getId());
- assertNotNull(shareTargets.get(1).getConversationData());
+ assertEquals("sc2", res.get(1).getId().getId());
+ assertEquals(CLASS_1, res.get(1).getClassName());
+ assertEquals(PACKAGE_1, res.get(1).getPackageName());
- assertEquals("sc3", shareTargets.get(2).getShareShortcutInfo().getShortcutInfo().getId());
- assertNotNull(shareTargets.get(2).getConversationData());
+ assertEquals("sc1", res.get(2).getId().getId());
+ assertEquals(CLASS_1, res.get(2).getClassName());
+ assertEquals(PACKAGE_1, res.get(2).getPackageName());
- assertEquals("sc4", shareTargets.get(3).getShareShortcutInfo().getShortcutInfo().getId());
- assertNull(shareTargets.get(3).getConversationData());
+ assertEquals("sc4", res.get(3).getId().getId());
+ assertEquals(CLASS_2, res.get(3).getClassName());
+ assertEquals(PACKAGE_2, res.get(3).getPackageName());
+ }
+
+ @Test
+ public void testPredictTargets_reachTargetsLimit() {
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc1"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc2"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc3"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc4"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_1, CLASS_1, "sc5"));
+ mShareShortcuts.add(buildShareShortcut(PACKAGE_2, CLASS_2, "sc6"));
+
+ when(mPackageData1.getConversationInfo("sc1")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc2")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc3")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc4")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData1.getConversationInfo("sc5")).thenReturn(mock(ConversationInfo.class));
+ when(mPackageData2.getConversationInfo("sc6")).thenReturn(mock(ConversationInfo.class));
+
+ when(mPackageData1.getEventHistory("sc1")).thenReturn(mEventHistory1);
+ when(mPackageData1.getEventHistory("sc2")).thenReturn(mEventHistory2);
+ when(mPackageData2.getEventHistory("sc3")).thenReturn(mEventHistory3);
+ when(mPackageData2.getEventHistory("sc4")).thenReturn(mEventHistory4);
+ when(mPackageData1.getEventHistory("sc5")).thenReturn(mEventHistory5);
+ when(mPackageData2.getEventHistory("sc6")).thenReturn(mEventHistory6);
+
+ when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+ when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+ when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+ when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+ when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+ when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6);
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
+ when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L));
+ when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(5L, 6L));
+ when(mEventIndex6.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(6L, 7L));
+
+ mPredictor.predictTargets();
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ List<AppTarget> res = mAppTargetCaptor.getValue();
+ assertEquals(5, res.size());
+
+ assertEquals("sc6", res.get(0).getId().getId());
+ assertEquals(CLASS_2, res.get(0).getClassName());
+ assertEquals(PACKAGE_2, res.get(0).getPackageName());
+
+ assertEquals("sc5", res.get(1).getId().getId());
+ assertEquals(CLASS_1, res.get(1).getClassName());
+ assertEquals(PACKAGE_1, res.get(1).getPackageName());
+
+ assertEquals("sc4", res.get(2).getId().getId());
+ assertEquals(CLASS_2, res.get(2).getClassName());
+ assertEquals(PACKAGE_2, res.get(2).getPackageName());
+
+ assertEquals("sc3", res.get(3).getId().getId());
+ assertEquals(CLASS_2, res.get(3).getClassName());
+ assertEquals(PACKAGE_2, res.get(3).getPackageName());
+
+ assertEquals("sc2", res.get(4).getId().getId());
+ assertEquals(CLASS_1, res.get(4).getClassName());
+ assertEquals(PACKAGE_1, res.get(4).getPackageName());
+ }
+
+ @Test
+ public void testSortTargets() {
+ AppTarget appTarget1 = new AppTarget.Builder(
+ new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+ .setClassName(CLASS_1)
+ .build();
+ AppTarget appTarget2 = new AppTarget.Builder(
+ new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+ .setClassName(CLASS_2)
+ .build();
+ AppTarget appTarget3 = new AppTarget.Builder(
+ new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+ .setClassName(CLASS_1)
+ .build();
+ AppTarget appTarget4 = new AppTarget.Builder(
+ new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+ .setClassName(CLASS_2)
+ .build();
+ AppTarget appTarget5 = new AppTarget.Builder(
+ new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
+ .setClassName(CLASS_1)
+ .build();
+
+ when(mPackageData1.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory1);
+ when(mPackageData1.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory2);
+ when(mPackageData2.getClassLevelEventHistory(CLASS_1)).thenReturn(mEventHistory3);
+ when(mPackageData2.getClassLevelEventHistory(CLASS_2)).thenReturn(mEventHistory4);
+ // PackageData of PACKAGE_3 is empty.
+ when(mDataManager.getPackage(PACKAGE_3, USER_ID)).thenReturn(null);
+
+ when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+ when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+ when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+ when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+ when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
+ when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
+ when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
+ when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(4L, 5L));
+
+ mPredictor.sortTargets(
+ List.of(appTarget1, appTarget2, appTarget3, appTarget4, appTarget5),
+ mUpdatePredictionsMethod);
+
+ verify(mUpdatePredictionsMethod).accept(mAppTargetCaptor.capture());
+ assertThat(mAppTargetCaptor.getValue()).containsExactly(
+ appTarget4, appTarget3, appTarget2, appTarget1, appTarget5);
}
private ShareShortcutInfo buildShareShortcut(
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9670658..5d5c714 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -29,12 +29,10 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
-import android.content.pm.parsing.PackageImpl;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedProvider;
import android.os.Build;
import android.os.Process;
import android.platform.test.annotations.Presubmit;
@@ -44,6 +42,9 @@
import androidx.annotation.NonNull;
import com.android.server.om.OverlayReferenceMapper;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.junit.Before;
import org.junit.Test;
@@ -74,7 +75,7 @@
private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>();
private static ParsingPackage pkg(String packageName) {
- return PackageImpl.forParsing(packageName)
+ return PackageImpl.forTesting(packageName)
.setTargetSdkVersion(Build.VERSION_CODES.R);
}
@@ -113,7 +114,7 @@
ParsedActivity activity = new ParsedActivity();
activity.setPackageName(packageName);
for (IntentFilter filter : filters) {
- final ParsedActivityIntentInfo info = new ParsedActivityIntentInfo(packageName, null);
+ final ParsedIntentInfo info = new ParsedIntentInfo();
if (filter.countActions() > 0) {
filter.actionsIterator().forEachRemaining(info::addAction);
}
@@ -127,7 +128,7 @@
filter.schemesIterator().forEachRemaining(info::addDataScheme);
}
activity.addIntent(info);
- activity.exported = true;
+ activity.setExported(true);
}
return pkg(packageName)
@@ -135,7 +136,7 @@
}
private static ParsingPackage pkgWithProvider(String packageName, String authority) {
- ComponentParseUtils.ParsedProvider provider = new ComponentParseUtils.ParsedProvider();
+ ParsedProvider provider = new ParsedProvider();
provider.setPackageName(packageName);
provider.setExported(true);
provider.setAuthority(authority);
@@ -437,7 +438,7 @@
ParsingPackage target = pkg("com.some.package.target")
.addOverlayable("overlayableName", actorName);
ParsingPackage overlay = pkg("com.some.package.overlay")
- .setIsOverlay(true)
+ .setOverlay(true)
.setOverlayTarget(target.getPackageName())
.setOverlayTargetName("overlayableName");
ParsingPackage actor = pkg("com.some.package.actor");
@@ -499,7 +500,7 @@
ParsingPackage target = pkg("com.some.package.target")
.addOverlayable("overlayableName", actorName);
ParsingPackage overlay = pkg("com.some.package.overlay")
- .setIsOverlay(true)
+ .setOverlay(true)
.setOverlayTarget(target.getPackageName())
.setOverlayTargetName("overlayableName");
ParsingPackage actorOne = pkg("com.some.package.actor.one");
@@ -618,7 +619,7 @@
private PackageSetting simulateAddPackage(AppsFilter filter,
ParsingPackage newPkgBuilder, int appId, @Nullable WithSettingBuilder action) {
- AndroidPackage newPkg = newPkgBuilder.hideAsParsed().hideAsFinal();
+ AndroidPackage newPkg = ((ParsedPackage) newPkgBuilder.hideAsParsed()).hideAsFinal();
final PackageSettingBuilder settingBuilder = new PackageSettingBuilder()
.setPackage(newPkg)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 56ac7c5..d2ec500 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import android.annotation.NonNull;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
@@ -31,32 +32,34 @@
import android.content.pm.PackageUserState;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils;
-import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
-import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
-import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
-import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
-import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
-import android.content.pm.parsing.ComponentParseUtils.ParsedService;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.PackageInfoUtils;
-import android.content.pm.parsing.ParsedPackage;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedInstrumentation;
+import android.content.pm.parsing.component.ParsedIntentInfo;
+import android.content.pm.parsing.component.ParsedPermission;
+import android.content.pm.parsing.component.ParsedPermissionGroup;
+import android.content.pm.parsing.component.ParsedProvider;
+import android.content.pm.parsing.component.ParsedService;
import android.os.Bundle;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import androidx.annotation.Nullable;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.PackageCacher;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.junit.Before;
import org.junit.Rule;
@@ -70,12 +73,12 @@
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+@Presubmit
@RunWith(AndroidJUnit4.class)
@MediumTest
public class PackageParserTest {
@@ -96,13 +99,13 @@
@Test
public void testParse_noCache() throws Exception {
- PackageParser pp = new CachePackageNameParser();
- ParsedPackage pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
+ CachePackageNameParser pp = new CachePackageNameParser(null, false, null, null, null);
+ ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
false /* useCaches */);
assertNotNull(pkg);
pp.setCacheDir(mTmpDir);
- pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
+ pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
false /* useCaches */);
assertNotNull(pkg);
@@ -113,39 +116,37 @@
@Test
public void testParse_withCache() throws Exception {
- PackageParser pp = new CachePackageNameParser();
+ CachePackageNameParser pp = new CachePackageNameParser(null, false, null, null, null);
pp.setCacheDir(mTmpDir);
// The first parse will write this package to the cache.
- pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
+ pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
// Now attempt to parse the package again, should return the
// cached result.
- ParsedPackage pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
+ ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
true /* useCaches */);
assertEquals("cache_android", pkg.getPackageName());
// Try again, with useCaches == false, shouldn't return the parsed
// result.
- pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
+ pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
assertEquals("android", pkg.getPackageName());
// We haven't set a cache directory here : the parse should still succeed,
// just not using the cached results.
- pp = new CachePackageNameParser();
- pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
+ pp = new CachePackageNameParser(null, false, null, null, null);
+ pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, true /* useCaches */);
assertEquals("android", pkg.getPackageName());
- pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
+ pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */, false /* useCaches */);
assertEquals("android", pkg.getPackageName());
}
@Test
public void test_serializePackage() throws Exception {
- PackageParser pp = new PackageParser();
- pp.setCacheDir(mTmpDir);
-
- ParsedPackage pkg = pp.parseParsedPackage(FRAMEWORK, 0 /* parseFlags */,
+ PackageParser2 pp = new PackageParser2(null, false, null, mTmpDir, null);
+ ParsedPackage pkg = pp.parsePackage(FRAMEWORK, 0 /* parseFlags */,
true /* useCaches */);
Parcel p = Parcel.obtain();
@@ -161,7 +162,7 @@
@SmallTest
@Presubmit
public void test_roundTripKnownFields() throws Exception {
- ParsingPackage pkg = PackageImpl.forParsing("foo");
+ ParsingPackage pkg = PackageImpl.forTesting("foo");
setKnownFields(pkg);
Parcel p = Parcel.obtain();
@@ -174,7 +175,7 @@
@Test
public void test_stringInterning() throws Exception {
- ParsingPackage pkg = PackageImpl.forParsing("foo");
+ ParsingPackage pkg = PackageImpl.forTesting("foo");
setKnownFields(pkg);
Parcel p = Parcel.obtain();
@@ -212,19 +213,44 @@
* A trivial subclass of package parser that only caches the package name, and throws away
* all other information.
*/
- public static class CachePackageNameParser extends PackageParser {
- @Override
- public byte[] toCacheEntry(ParsedPackage pkg) {
- return ("cache_" + pkg.getPackageName()).getBytes(StandardCharsets.UTF_8);
+ public static class CachePackageNameParser extends PackageParser2 {
+
+ CachePackageNameParser(String[] separateProcesses, boolean onlyCoreApps,
+ DisplayMetrics displayMetrics, @Nullable File cacheDir,
+ PackageParser2.Callback callback) {
+ super(separateProcesses, onlyCoreApps, displayMetrics, cacheDir, callback);
+ if (cacheDir != null) {
+ setCacheDir(cacheDir);
+ }
}
- @Override
- public ParsedPackage fromCacheEntry(byte[] cacheEntry) {
- return PackageImpl.forParsing(new String(cacheEntry, StandardCharsets.UTF_8))
- .hideAsParsed();
+ void setCacheDir(@NonNull File cacheDir) {
+ this.mCacher = new PackageCacher(cacheDir) {
+ @Override
+ public byte[] toCacheEntry(ParsedPackage pkg) {
+ return ("cache_" + pkg.getPackageName()).getBytes(StandardCharsets.UTF_8);
+ }
+
+ @Override
+ public ParsedPackage fromCacheEntry(byte[] cacheEntry) {
+ return ((ParsedPackage) PackageImpl.forTesting(
+ new String(cacheEntry, StandardCharsets.UTF_8))
+ .hideAsParsed());
+ }
+ };
}
}
+ private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
+ return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
+ new File(pkg.getCodePath()), new File(pkg.getCodePath()), null,
+ pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
+ null, pkg.getVersionCode(),
+ PackageInfoUtils.appInfoFlags(pkg, null),
+ PackageInfoUtils.appInfoPrivateFlags(pkg, null),
+ pkg.getSharedUserLabel(), null, null, null);
+ }
+
// NOTE: The equality assertions below are based on code autogenerated by IntelliJ.
public static void assertPackagesEqual(AndroidPackage a, AndroidPackage b) {
@@ -232,7 +258,6 @@
assertEquals(a.isBaseHardwareAccelerated(), b.isBaseHardwareAccelerated());
assertEquals(a.getVersionCode(), b.getVersionCode());
assertEquals(a.getSharedUserLabel(), b.getSharedUserLabel());
- assertEquals(a.getPreferredOrder(), b.getPreferredOrder());
assertEquals(a.getInstallLocation(), b.getInstallLocation());
assertEquals(a.isCoreApp(), b.isCoreApp());
assertEquals(a.isRequiredForAllUsers(), b.isRequiredForAllUsers());
@@ -249,9 +274,9 @@
assertArrayEquals(a.getSplitFlags(), b.getSplitFlags());
PackageInfo aInfo = PackageInfoUtils.generate(a, new int[]{}, 0, 0, 0,
- Collections.emptySet(), new PackageUserState(), 0);
+ Collections.emptySet(), new PackageUserState(), 0, mockPkgSetting(a));
PackageInfo bInfo = PackageInfoUtils.generate(b, new int[]{}, 0, 0, 0,
- Collections.emptySet(), new PackageUserState(), 0);
+ Collections.emptySet(), new PackageUserState(), 0, mockPkgSetting(b));
assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
assertEquals(ArrayUtils.size(a.getPermissions()), ArrayUtils.size(b.getPermissions()));
@@ -298,15 +323,13 @@
assertEquals(a.getLibraryNames(), b.getLibraryNames());
assertEquals(a.getUsesLibraries(), b.getUsesLibraries());
assertEquals(a.getUsesOptionalLibraries(), b.getUsesOptionalLibraries());
- assertArrayEquals(a.getUsesLibraryFiles(), b.getUsesLibraryFiles());
assertEquals(a.getOriginalPackages(), b.getOriginalPackages());
assertEquals(a.getRealPackage(), b.getRealPackage());
assertEquals(a.getAdoptPermissions(), b.getAdoptPermissions());
- assertBundleApproximateEquals(a.getAppMetaData(), b.getAppMetaData());
+ assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
assertEquals(a.getVersionName(), b.getVersionName());
assertEquals(a.getSharedUserId(), b.getSharedUserId());
assertArrayEquals(a.getSigningDetails().signatures, b.getSigningDetails().signatures);
- assertArrayEquals(a.getLastPackageUsageTimeInMills(), b.getLastPackageUsageTimeInMills());
assertEquals(a.getRestrictedAccountType(), b.getRestrictedAccountType());
assertEquals(a.getRequiredAccountType(), b.getRequiredAccountType());
assertEquals(a.getOverlayTarget(), b.getOverlayTarget());
@@ -317,7 +340,6 @@
assertEquals(a.getSigningDetails().publicKeys, b.getSigningDetails().publicKeys);
assertEquals(a.getUpgradeKeySets(), b.getUpgradeKeySets());
assertEquals(a.getKeySetMapping(), b.getKeySetMapping());
- assertEquals(a.getCpuAbiOverride(), b.getCpuAbiOverride());
assertArrayEquals(a.getRestrictUpdateHash(), b.getRestrictUpdateHash());
}
@@ -333,44 +355,42 @@
assertEquals(a.toString(), b.toString());
}
- private static void assertComponentsEqual(ParsedComponent<?> a,
- ParsedComponent<?> b) {
- assertEquals(a.className, b.className);
+ private static void assertComponentsEqual(ParsedComponent a, ParsedComponent b) {
+ assertEquals(a.getName(), b.getName());
assertBundleApproximateEquals(a.getMetaData(), b.getMetaData());
assertEquals(a.getComponentName(), b.getComponentName());
- if (a.intents != null && b.intents != null) {
- assertEquals(a.intents.size(), b.intents.size());
- } else if (a.intents == null || b.intents == null) {
+ if (a.getIntents() != null && b.getIntents() != null) {
+ assertEquals(a.getIntents().size(), b.getIntents().size());
+ } else if (a.getIntents() == null || b.getIntents() == null) {
return;
}
- for (int i = 0; i < a.intents.size(); ++i) {
- ParsedIntentInfo aIntent = a.intents.get(i);
- ParsedIntentInfo bIntent = b.intents.get(i);
+ for (int i = 0; i < a.getIntents().size(); ++i) {
+ ParsedIntentInfo aIntent = a.getIntents().get(i);
+ ParsedIntentInfo bIntent = b.getIntents().get(i);
- assertEquals(aIntent.hasDefault, bIntent.hasDefault);
- assertEquals(aIntent.labelRes, bIntent.labelRes);
- assertEquals(aIntent.nonLocalizedLabel, bIntent.nonLocalizedLabel);
- assertEquals(aIntent.icon, bIntent.icon);
+ assertEquals(aIntent.isHasDefault(), bIntent.isHasDefault());
+ assertEquals(aIntent.getLabelRes(), bIntent.getLabelRes());
+ assertEquals(aIntent.getNonLocalizedLabel(), bIntent.getNonLocalizedLabel());
+ assertEquals(aIntent.getIcon(), bIntent.getIcon());
}
}
- private static void assertPermissionsEqual(ParsedPermission a,
- ParsedPermission b) {
+ private static void assertPermissionsEqual(ParsedPermission a, ParsedPermission b) {
assertComponentsEqual(a, b);
- assertEquals(a.tree, b.tree);
+ assertEquals(a.isTree(), b.isTree());
// Verify basic flags in PermissionInfo to make sure they're consistent. We don't perform
// a full structural equality here because the code that serializes them isn't parser
// specific and is tested elsewhere.
assertEquals(a.getProtection(), b.getProtection());
assertEquals(a.getGroup(), b.getGroup());
- assertEquals(a.flags, b.flags);
+ assertEquals(a.getFlags(), b.getFlags());
- if (a.parsedPermissionGroup != null && b.parsedPermissionGroup != null) {
- assertPermissionGroupsEqual(a.parsedPermissionGroup, b.parsedPermissionGroup);
- } else if (a.parsedPermissionGroup != null || b.parsedPermissionGroup != null) {
+ if (a.getParsedPermissionGroup() != null && b.getParsedPermissionGroup() != null) {
+ assertPermissionGroupsEqual(a.getParsedPermissionGroup(), b.getParsedPermissionGroup());
+ } else if (a.getParsedPermissionGroup() != null || b.getParsedPermissionGroup() != null) {
throw new AssertionError();
}
}
@@ -382,6 +402,8 @@
// Sanity check for InstrumentationInfo.
assertEquals(a.getTargetPackage(), b.getTargetPackage());
assertEquals(a.getTargetProcesses(), b.getTargetProcesses());
+ assertEquals(a.isHandleProfiling(), b.isHandleProfiling());
+ assertEquals(a.isFunctionalTest(), b.isFunctionalTest());
}
private static void assertServicesEqual(
@@ -393,10 +415,10 @@
assertComponentsEqual(a, b);
// Sanity check for ServiceInfo.
- ServiceInfo aInfo = PackageInfoUtils.generateServiceInfo(aPkg, a, 0, new PackageUserState(),
- 0);
- ServiceInfo bInfo = PackageInfoUtils.generateServiceInfo(bPkg, b, 0, new PackageUserState(),
- 0);
+ ServiceInfo aInfo = PackageInfoUtils.generateServiceInfo(aPkg, a, 0,
+ new PackageUserState(), 0, mockPkgSetting(aPkg));
+ ServiceInfo bInfo = PackageInfoUtils.generateServiceInfo(bPkg, b, 0,
+ new PackageUserState(), 0, mockPkgSetting(bPkg));
assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
assertEquals(a.getName(), b.getName());
}
@@ -411,9 +433,9 @@
// Sanity check for ProviderInfo
ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0,
- new PackageUserState(), 0);
+ new PackageUserState(), 0, mockPkgSetting(aPkg));
ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0,
- new PackageUserState(), 0);
+ new PackageUserState(), 0, mockPkgSetting(bPkg));
assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
assertEquals(a.getName(), b.getName());
}
@@ -428,9 +450,9 @@
// Sanity check for ActivityInfo.
ActivityInfo aInfo = PackageInfoUtils.generateActivityInfo(aPkg, a, 0,
- new PackageUserState(), 0);
+ new PackageUserState(), 0, mockPkgSetting(aPkg));
ActivityInfo bInfo = PackageInfoUtils.generateActivityInfo(bPkg, b, 0,
- new PackageUserState(), 0);
+ new PackageUserState(), 0, mockPkgSetting(bPkg));
assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo);
assertEquals(a.getName(), b.getName());
}
@@ -441,7 +463,7 @@
// Sanity check for PermissionGroupInfo.
assertEquals(a.getName(), b.getName());
- assertEquals(a.descriptionRes, b.descriptionRes);
+ assertEquals(a.getDescriptionRes(), b.getDescriptionRes());
}
private static void assertApplicationInfoEqual(ApplicationInfo a, ApplicationInfo that) {
@@ -494,12 +516,11 @@
bundle.putString("key", "value");
ParsedPermission permission = new ParsedPermission();
- permission.parsedPermissionGroup = new ParsedPermissionGroup();
+ permission.setParsedPermissionGroup(new ParsedPermissionGroup());
- pkg.setBaseRevisionCode(100)
+ ((ParsedPackage) pkg.setBaseRevisionCode(100)
.setBaseHardwareAccelerated(true)
.setSharedUserLabel(100)
- .setPreferredOrder(100)
.setInstallLocation(100)
.setRequiredForAllUsers(true)
.asSplit(
@@ -510,7 +531,6 @@
)
.setUse32BitAbi(true)
.setVolumeUuid("foo3")
- .setCodePath("foo4")
.addPermission(permission)
.addPermissionGroup(new ParsedPermissionGroup())
.addActivity(new ParsedActivity())
@@ -532,7 +552,7 @@
.addOriginalPackage("foo14")
.setRealPackage("foo15")
.addAdoptPermission("foo16")
- .setAppMetaData(bundle)
+ .setMetaData(bundle)
.setVersionName("foo17")
.setSharedUserId("foo18")
.setSigningDetails(
@@ -547,8 +567,7 @@
.setOverlayTarget("foo21")
.setOverlayPriority(100)
.setUpgradeKeySets(new ArraySet<>())
- .addPreferredActivityFilter(
- new ComponentParseUtils.ParsedActivityIntentInfo("foo", "className"))
+ .addPreferredActivityFilter("className", new ParsedIntentInfo())
.addConfigPreference(new ConfigurationInfo())
.addReqFeature(new FeatureInfo())
.addFeatureGroup(new FeatureGroupInfo())
@@ -559,19 +578,14 @@
.setOverlayTargetName("foo26")
.setVisibleToInstantApps(true)
.setSplitHasCode(0, true)
- .hideAsParsed()
+ .hideAsParsed())
.setBaseCodePath("foo5")
+ .setCodePath("foo4")
.setVersionCode(100)
- .setCpuAbiOverride("foo22")
.setRestrictUpdateHash(new byte[16])
.setVersionCodeMajor(100)
.setCoreApp(true)
- .hideAsFinal()
- .mutate()
- .setUsesLibraryInfos(Arrays.asList(
- new SharedLibraryInfo(null, null, null, null, 0L, 0, null, null, null)
- ))
- .setUsesLibraryFiles(new String[]{"foo13"});
+ .hideAsFinal();
}
private static void assertAllFieldsExist(ParsedPackage pkg) throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 841cea2..d12ea894 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -18,10 +18,11 @@
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
-import android.content.pm.parsing.AndroidPackage;
import android.util.ArraySet;
import android.util.SparseArray;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+
import java.io.File;
import java.util.Map;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
index da5986f7..9d3ac17 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java
@@ -17,11 +17,14 @@
package com.android.server.pm;
import android.content.pm.PackageParser;
-import android.content.pm.parsing.ParsedPackage;
+import android.platform.test.annotations.Presubmit;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import junit.framework.Assert;
import org.junit.Before;
@@ -36,6 +39,7 @@
/**
* Tests for {@link ParallelPackageParser}
*/
+@Presubmit
@RunWith(AndroidJUnit4.class)
public class ParallelPackageParserTest {
private static final String TAG = ParallelPackageParserTest.class.getSimpleName();
@@ -44,7 +48,7 @@
@Before
public void setUp() {
- mParser = new TestParallelPackageParser(new PackageParser(),
+ mParser = new TestParallelPackageParser(new PackageParser2(null, false, null, null, null),
ParallelPackageParser.makeExecutorService());
}
@@ -72,7 +76,7 @@
private class TestParallelPackageParser extends ParallelPackageParser {
- TestParallelPackageParser(PackageParser packageParser, ExecutorService executorService) {
+ TestParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
super(packageParser, executorService);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index 30108c6..efc1c05 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -22,12 +22,13 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
import android.os.Build;
import android.platform.test.annotations.Presubmit;
import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -79,9 +80,9 @@
}
private AndroidPackage makePackage(int targetSdkVersion) {
- return PackageImpl.forParsing(PACKAGE_NAME)
+ return ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(targetSdkVersion)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
index 11f154b..12fb400 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java
@@ -16,10 +16,13 @@
package com.android.server.pm;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ParsedPackage;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.UserHandle;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
class ScanRequestBuilder {
private final ParsedPackage mPkg;
private AndroidPackage mOldPkg;
@@ -32,6 +35,8 @@
private int mScanFlags;
private UserHandle mUser;
private boolean mIsPlatformPackage;
+ @Nullable
+ private String mCpuAbiOverride;
ScanRequestBuilder(ParsedPackage pkg) {
this.mPkg = pkg;
@@ -97,10 +102,16 @@
return this;
}
+ @NonNull
+ public ScanRequestBuilder setCpuAbiOverride(@Nullable String cpuAbiOverride) {
+ this.mCpuAbiOverride = cpuAbiOverride;
+ return this;
+ }
+
PackageManagerService.ScanRequest build() {
return new PackageManagerService.ScanRequest(
mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting,
mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage,
- mUser);
+ mUser, mCpuAbiOverride);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 583cf58..09f946d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -42,9 +42,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.PackageInfoUtils;
-import android.content.pm.parsing.ParsedPackage;
import android.content.pm.parsing.ParsingPackage;
import android.content.res.TypedArray;
import android.os.Environment;
@@ -54,6 +51,10 @@
import android.util.Pair;
import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -101,18 +102,18 @@
@Before
public void setupDefaultAbiBehavior() throws Exception {
when(mMockPackageAbiHelper.derivePackageAbi(
- any(ParsedPackage.class), nullable(String.class), anyBoolean()))
+ any(AndroidPackage.class), anyBoolean(), nullable(String.class), anyBoolean()))
.thenReturn(new Pair<>(
new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
new PackageAbiHelper.NativeLibraryPaths(
"derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2")));
when(mMockPackageAbiHelper.getNativeLibraryPaths(
- any(ParsedPackage.class), any(File.class)))
+ any(AndroidPackage.class), any(PackageSetting.class), any(File.class)))
.thenReturn(new PackageAbiHelper.NativeLibraryPaths(
"getRootDir", true, "getNativeDir", "getNativeDir2"
));
when(mMockPackageAbiHelper.getBundledAppAbis(
- any(ParsedPackage.class)))
+ any(AndroidPackage.class)))
.thenReturn(new PackageAbiHelper.Abis("bundledPrimary", "bundledSecondary"));
}
@@ -223,10 +224,10 @@
@Test
public void installStaticSharedLibrary() throws Exception {
- final ParsedPackage pkg = createBasicPackage("static.lib.pkg")
+ final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("static.lib.pkg")
.setStaticSharedLibName("static.lib")
.setStaticSharedLibVersion(123L)
- .hideAsParsed()
+ .hideAsParsed())
.setPackageName("static.lib.pkg.123")
.setVersionCodeMajor(1)
.setVersionCode(234)
@@ -255,10 +256,11 @@
@Test
public void installDynamicLibraries() throws Exception {
- final ParsedPackage pkg = createBasicPackage("dynamic.lib.pkg")
+ final ParsedPackage pkg = ((ParsedPackage) createBasicPackage(
+ "dynamic.lib.pkg")
.addLibraryName("liba")
.addLibraryName("libb")
- .hideAsParsed()
+ .hideAsParsed())
.setVersionCodeMajor(1)
.setVersionCode(234)
.setBaseCodePath("/some/path.apk")
@@ -304,9 +306,9 @@
.setVolumeUuid("someUuid")
.build();
- final ParsedPackage basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME)
- .hideAsParsed()
- .setApplicationVolumeUuid(UUID_TWO.toString());
+ final ParsedPackage basicPackage = ((ParsedPackage) createBasicPackage(DUMMY_PACKAGE_NAME)
+ .setVolumeUuid(UUID_TWO.toString())
+ .hideAsParsed());
final PackageManagerService.ScanResult scanResult = executeScan(
@@ -321,9 +323,8 @@
createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME).build();
final ParsedPackage basicPackage =
- createBasicPackage(DUMMY_PACKAGE_NAME)
- .hideAsParsed()
- .setCpuAbiOverride("testOverride");
+ ((ParsedPackage) createBasicPackage(DUMMY_PACKAGE_NAME)
+ .hideAsParsed());
final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder(
@@ -341,7 +342,7 @@
createBasicPackageSettingBuilder("original.package").build();
final ParsedPackage basicPackage =
- createBasicPackage(DUMMY_PACKAGE_NAME)
+ (ParsedPackage) createBasicPackage(DUMMY_PACKAGE_NAME)
.hideAsParsed();
@@ -411,8 +412,9 @@
final PackageManagerService.ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.request.parsedPackage.getFlags(),
- hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
+ int appInfoFlags = PackageInfoUtils.appInfoFlags(scanResult.request.parsedPackage,
+ scanResult.pkgSetting);
+ assertThat(appInfoFlags, hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP));
}
@Test
@@ -426,14 +428,15 @@
true /*isUnderFactoryTest*/,
System.currentTimeMillis());
- assertThat(scanResult.request.parsedPackage.getFlags(),
- hasFlag(ApplicationInfo.FLAG_FACTORY_TEST));
+ int appInfoFlags = PackageInfoUtils.appInfoFlags(scanResult.request.parsedPackage,
+ scanResult.request.pkgSetting);
+ assertThat(appInfoFlags, hasFlag(ApplicationInfo.FLAG_FACTORY_TEST));
}
@Test
public void scanSystemApp_isOrphanedTrue() throws Exception {
- final ParsedPackage pkg = createBasicPackage(DUMMY_PACKAGE_NAME)
- .hideAsParsed()
+ final ParsedPackage pkg = ((ParsedPackage) createBasicPackage(DUMMY_PACKAGE_NAME)
+ .hideAsParsed())
.setSystem(true);
final PackageManagerService.ScanRequest scanRequest =
@@ -474,10 +477,6 @@
System.currentTimeMillis());
}
- private static String createResourcePath(String packageName) {
- return "/data/app/" + packageName + "-randompath/base.apk";
- }
-
private static String createCodePath(String packageName) {
return "/data/app/" + packageName + "-randompath";
}
@@ -486,11 +485,11 @@
return new PackageSettingBuilder()
.setName(packageName)
.setCodePath(createCodePath(packageName))
- .setResourcePath(createResourcePath(packageName));
+ .setResourcePath(createCodePath(packageName));
}
private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) {
- return new ScanRequestBuilder(pkg.hideAsParsed())
+ return new ScanRequestBuilder((ParsedPackage) pkg.hideAsParsed())
.setUser(UserHandle.of(0));
}
@@ -501,16 +500,15 @@
private static ParsingPackage createBasicPackage(String packageName) {
// TODO(b/135203078): Make this use PackageImpl.forParsing and separate the steps
- return new PackageImpl(packageName, null, mock(TypedArray.class), false)
- .setCodePath("/data/tmp/randompath")
- .setApplicationInfoCodePath(createCodePath(packageName))
- .setApplicationInfoResourcePath(createResourcePath(packageName))
- .setApplicationVolumeUuid(UUID_ONE.toString())
- .setBaseCodePath("/data/tmp/randompath/base.apk")
+ return (ParsingPackage) ((ParsedPackage) new PackageImpl(packageName,
+ "/data/tmp/randompath/base.apk", createCodePath(packageName),
+ mock(TypedArray.class), false)
+ .setVolumeUuid(UUID_ONE.toString())
.addUsesStaticLibrary("some.static.library")
.addUsesStaticLibraryVersion(234L)
.addUsesStaticLibrary("some.other.static.library")
.addUsesStaticLibraryVersion(456L)
+ .hideAsParsed())
.setNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
.setVersionCodeMajor(1)
.setVersionCode(2345);
@@ -524,7 +522,7 @@
assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
- pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0);
+ pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertBasicApplicationInfo(scanResult, applicationInfo);
}
@@ -537,7 +535,7 @@
assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
- assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName))));
+ assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName))));
assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
}
@@ -559,7 +557,7 @@
private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) {
PackageSetting pkgSetting = scanResult.pkgSetting;
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
- pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0);
+ pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary"));
assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary"));
@@ -573,7 +571,7 @@
private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) {
PackageSetting pkgSetting = scanResult.pkgSetting;
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
- pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0);
+ pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir"));
assertThat(pkgSetting.legacyNativeLibraryPathString, is("getRootDir"));
assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 2936bdd..56460fb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -379,6 +379,113 @@
});
}
+ public void testPushDynamicShortcut() {
+
+ setCaller(CALLING_PACKAGE_1, USER_0);
+
+ final ShortcutInfo s1 = makeShortcut("s1");
+ final ShortcutInfo s2 = makeShortcut("s2");
+ final ShortcutInfo s3 = makeShortcut("s3");
+ final ShortcutInfo s4 = makeShortcut("s4");
+
+ final ShortcutInfo s10 = makeShortcut("s10");
+ final ShortcutInfo s11 = makeShortcut("s11");
+ final ShortcutInfo s12 = makeShortcut("s12");
+ final ShortcutInfo s13 = makeShortcut("s13");
+ final ShortcutInfo s14 = makeShortcut("s14");
+
+ // Test push as first shortcut
+ mManager.pushDynamicShortcut(s1);
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1");
+ assertEquals(0, getCallerShortcut("s1").getRank());
+
+ // Test push when other shortcuts exist
+ assertTrue(mManager.setDynamicShortcuts(list(s1, s2)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1", "s2");
+ mManager.pushDynamicShortcut(s3);
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ assertEquals(0, getCallerShortcut("s3").getRank());
+ assertEquals(1, getCallerShortcut("s1").getRank());
+ assertEquals(2, getCallerShortcut("s2").getRank());
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset
+
+ // Push with set rank
+ s4.setRank(2);
+ mManager.pushDynamicShortcut(s4);
+ assertEquals(2, getCallerShortcut("s4").getRank());
+ assertEquals(3, getCallerShortcut("s2").getRank());
+
+ // Push existing shortcut with set rank
+ final ShortcutInfo s4_2 = makeShortcut("s4");
+ s4_2.setRank(4);
+ mManager.pushDynamicShortcut(s4_2);
+ assertEquals(2, getCallerShortcut("s2").getRank());
+ assertEquals(3, getCallerShortcut("s4").getRank());
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset
+
+ // Test push as last
+ assertTrue(mManager.addDynamicShortcuts(makeShortcuts("s5", "s6", "s7", "s8", "s9")));
+ mManager.pushDynamicShortcut(s10);
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10");
+ assertEquals(0, getCallerShortcut("s10").getRank());
+ assertEquals(1, getCallerShortcut("s5").getRank());
+ assertEquals(6, getCallerShortcut("s3").getRank());
+ assertEquals(7, getCallerShortcut("s1").getRank());
+ assertEquals(8, getCallerShortcut("s2").getRank());
+ assertEquals(9, getCallerShortcut("s4").getRank());
+
+ // Push when max has already reached
+ mManager.pushDynamicShortcut(s11);
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3", "s5", "s6", "s7", "s8", "s9", "s10", "s11");
+ assertEquals(0, getCallerShortcut("s11").getRank());
+ assertEquals(1, getCallerShortcut("s10").getRank());
+ assertEquals(9, getCallerShortcut("s2").getRank());
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset
+
+ // Push with different activity
+ s12.setActivity(makeComponent(ShortcutActivity2.class));
+ mManager.pushDynamicShortcut(s12);
+ assertEquals(makeComponent(ShortcutActivity2.class),
+ getCallerShortcut("s12").getActivity());
+ assertEquals(0, getCallerShortcut("s12").getRank());
+
+ // Push to update shortcut with different activity
+ final ShortcutInfo s1_2 = makeShortcut("s1");
+ s1_2.setActivity(makeComponent(ShortcutActivity2.class));
+ s1_2.setRank(1);
+ mManager.pushDynamicShortcut(s1_2);
+ assertEquals(0, getCallerShortcut("s12").getRank());
+ assertEquals(1, getCallerShortcut("s1").getRank());
+ assertEquals(0, getCallerShortcut("s11").getRank());
+ assertEquals(1, getCallerShortcut("s10").getRank());
+ assertEquals(7, getCallerShortcut("s3").getRank());
+ assertEquals(8, getCallerShortcut("s2").getRank());
+
+ mInjectedCurrentTimeMillis += INTERVAL; // reset
+
+ // Test push when dropped shortcut is cached
+ s13.setLongLived();
+ s13.setRank(100);
+ mManager.pushDynamicShortcut(s13);
+ assertEquals(9, getCallerShortcut("s13").getRank());
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s13"), HANDLE_USER_0);
+ });
+
+ mManager.pushDynamicShortcut(s14);
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s14");
+ // Verify s13 stayed as cached
+ assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+ "s13");
+ }
+
public void testUnlimitedCalls() {
setCaller(CALLING_PACKAGE_1, USER_0);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 118c540..bec37e9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -812,7 +812,7 @@
"android", 0, mUserManager.getPrimaryUser().getUserHandle())
.getSystemService(Context.USER_SERVICE);
- List<UserHandle> profiles = um.getUserProfiles(false);
+ List<UserHandle> profiles = um.getAllProfiles();
assertThat(profiles.size()).isEqualTo(2);
assertThat(profiles.get(0).equals(userProfile.getUserHandle())
|| profiles.get(1).equals(userProfile.getUserHandle())).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 3db832b..ce21099 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -39,8 +39,6 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserManager;
@@ -56,6 +54,9 @@
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.junit.After;
import org.junit.Before;
@@ -252,14 +253,14 @@
final Set<String> userWhitelist = new ArraySet<>();
userWhitelist.add(packageName1);
- final AndroidPackage pkg1 = PackageImpl.forParsing(packageName1)
- .hideAsParsed().hideAsFinal();
- final AndroidPackage pkg2 = PackageImpl.forParsing(packageName2)
- .hideAsParsed().hideAsFinal();
- final AndroidPackage pkg3 = PackageImpl.forParsing(packageName3)
- .hideAsParsed().hideAsFinal();
- final AndroidPackage pkg4 = PackageImpl.forParsing(packageName4)
- .hideAsParsed().hideAsFinal();
+ final AndroidPackage pkg1 = ((ParsedPackage) PackageImpl.forTesting(packageName1)
+ .hideAsParsed()).hideAsFinal();
+ final AndroidPackage pkg2 = ((ParsedPackage) PackageImpl.forTesting(packageName2)
+ .hideAsParsed()).hideAsFinal();
+ final AndroidPackage pkg3 = ((ParsedPackage) PackageImpl.forTesting(packageName3)
+ .hideAsParsed()).hideAsFinal();
+ final AndroidPackage pkg4 = ((ParsedPackage) PackageImpl.forTesting(packageName4)
+ .hideAsParsed()).hideAsFinal();
// No implicit whitelist, so only install pkg1.
boolean implicit = false;
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index f5e5e2a..d69e1b8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -532,6 +532,61 @@
assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
}
+ @Test
+ public void testPrimaryAndSecondaryDexLoad() {
+ // Foo loads both primary and secondary dexes
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+ List<String> fooDexes = new ArrayList<>(mFooUser0.getBaseAndSplitDexPaths());
+ int primaryCount = fooDexes.size();
+ fooDexes.addAll(fooSecondaries);
+
+ notifyDexLoad(mFooUser0, fooDexes, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+
+ // Below we want to verify that the secondary dex files within fooDexes have been correctly
+ // reported and their class loader contexts were correctly recorded.
+ //
+ // In order to achieve this we first use DexoptUtils.processContextForDexLoad to compute the
+ // class loader contexts for all the dex files.
+ String[] allClassLoaderContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(mFooUser0.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, fooDexes)));
+ // Next we filter out the class loader contexts corresponding to non-secondary dex files.
+ String[] secondaryClassLoaderContexts = Arrays.copyOfRange(allClassLoaderContexts,
+ primaryCount, allClassLoaderContexts.length);
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+ secondaryClassLoaderContexts);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+ }
+
+ @Test
+ public void testNotifySecondary_withSharedLibrary() {
+ // Foo loads its own secondary files.
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+
+ String contextSuffix = "{PCL[/system/framework/org.apache.http.legacy.jar]}";
+ String[] expectedContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(mFooUser0.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, fooSecondaries)));
+ for (int i = 0; i < expectedContexts.length; i++) {
+ expectedContexts[i] += contextSuffix;
+ }
+
+ notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+ expectedContexts);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
@@ -572,17 +627,43 @@
// By default, assume a single class loader in the chain.
// This makes writing tests much easier.
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
- List<String> classPaths = (dexPaths == null)
- ? Arrays.asList((String) null)
- : Arrays.asList(String.join(File.pathSeparator, dexPaths));
+ List<String> classPaths = dexPaths != null
+ ? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
}
private void notifyDexLoad(TestData testData, List<String> classLoaders,
List<String> classPaths, int loaderUserId) {
+ String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
// We call the internal function so any exceptions thrown cause test failures.
- mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders,
- classPaths, testData.mLoaderIsa, loaderUserId);
+ List<String> dexPaths = classPaths != null
+ ? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
+ notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
+ }
+
+ private void notifyDexLoad(TestData testData, List<String> dexPaths,
+ String[] classLoaderContexts, int loaderUserId) {
+ assertTrue(dexPaths.size() == classLoaderContexts.length);
+ HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
+ for (int i = 0; i < dexPaths.size(); i++) {
+ dexPathMapping.put(dexPaths.get(i), classLoaderContexts != null
+ ? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ }
+ mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
+ testData.mLoaderIsa, loaderUserId);
+ }
+
+ private String[] computeClassLoaderContexts(List<String> classLoaders,
+ List<String> classPaths) {
+ if (classPaths == null) {
+ return new String[0];
+ }
+ String[] results = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+ if (results == null) {
+ results = new String[classPaths.get(0).split(File.pathSeparator).length];
+ Arrays.fill(results, PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ }
+ return results;
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
similarity index 86%
rename from core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
rename to services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index 1e0bfb0..f87f68d 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -1,5 +1,5 @@
-/**
- * Copyright (C) 2018 The Android Open Source Project
+/*
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.content.pm.dex;
+package com.android.server.pm.dex;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -27,14 +27,17 @@
import android.content.pm.PackageParser.ApkLite;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.dex.DexMetadataHelper;
import android.os.FileUtils;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.coretests.R;
+import com.android.frameworks.servicestests.R;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.junit.Assert;
import org.junit.Before;
@@ -90,13 +93,17 @@
return outFile;
}
+ private PackageParser2 makeParser() {
+ return new PackageParser2(null, false, null, null, null);
+ }
+
@Test
public void testParsePackageWithDmFileValid() throws IOException, PackageParserException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
createDexMetadataFile("install_split_base.apk");
- ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
+ ParsedPackage pkg = makeParser().parsePackage(mTmpDir, 0 /* flags */, false);
- Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
assertEquals(1, packageDexMetadata.size());
String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
assertNotNull(baseDexMetadata);
@@ -110,9 +117,9 @@
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_base.apk");
createDexMetadataFile("install_split_feature_a.apk");
- ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
+ ParsedPackage pkg = makeParser().parsePackage(mTmpDir, 0 /* flags */, false);
- Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
assertEquals(2, packageDexMetadata.size());
String baseDexMetadata = packageDexMetadata.get(pkg.getBaseCodePath());
assertNotNull(baseDexMetadata);
@@ -129,9 +136,9 @@
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
copyApkToToTmpDir("install_split_feature_a.apk", R.raw.install_split_feature_a);
createDexMetadataFile("install_split_feature_a.apk");
- ParsedPackage pkg = new PackageParser().parseParsedPackage(mTmpDir, 0 /* flags */, false);
+ ParsedPackage pkg = makeParser().parsePackage(mTmpDir, 0 /* flags */, false);
- Map<String, String> packageDexMetadata = DexMetadataHelper.getPackageDexMetadata(pkg);
+ Map<String, String> packageDexMetadata = AndroidPackageUtils.getPackageDexMetadata(pkg);
assertEquals(1, packageDexMetadata.size());
String splitDexMetadata = packageDexMetadata.get(pkg.getSplitCodePaths()[0]);
@@ -145,9 +152,8 @@
File invalidDmFile = new File(mTmpDir, "install_split_base.dm");
Files.createFile(invalidDmFile.toPath());
try {
- ParsedPackage pkg = new PackageParser()
- .parseParsedPackage(mTmpDir, 0 /* flags */, false);
- DexMetadataHelper.validatePackageDexMetadata(pkg);
+ ParsedPackage pkg = makeParser().parsePackage(mTmpDir, 0 /* flags */, false);
+ AndroidPackageUtils.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
@@ -163,9 +169,8 @@
Files.createFile(invalidDmFile.toPath());
try {
- ParsedPackage pkg = new PackageParser()
- .parseParsedPackage(mTmpDir, 0 /* flags */, false);
- DexMetadataHelper.validatePackageDexMetadata(pkg);
+ ParsedPackage pkg = makeParser().parsePackage(mTmpDir, 0 /* flags */, false);
+ AndroidPackageUtils.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
assertEquals(e.error, PackageManager.INSTALL_FAILED_BAD_DEX_METADATA);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 66a4946..3846be0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -23,15 +23,16 @@
import static org.junit.Assert.fail;
import android.content.pm.SharedLibraryInfo;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
import android.content.pm.parsing.ParsingPackage;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
@@ -61,7 +62,8 @@
private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
boolean addSplitDependencies, boolean isolatedSplitLoading) {
String codeDir = "/data/app/mock.android.com";
- ParsingPackage parsingPackage = PackageImpl.forParsing("mock.android.com")
+ ParsingPackage parsingPackage = PackageImpl.forTesting("mock.android.com",
+ codeDir + "/base.dex")
.setClassLoaderName(baseClassLoader);
parsingPackage.setIsolatedSplitLoading(isolatedSplitLoading);
@@ -122,8 +124,7 @@
.setSplitClassLoaderName(7, null);
}
- ParsedPackage parsedPackage = parsingPackage.hideAsParsed()
- .setBaseCodePath(codeDir + "/base.dex");
+ ParsedPackage parsedPackage = (ParsedPackage) parsingPackage.hideAsParsed();
TestData data = new TestData();
data.pkg = parsedPackage.hideAsFinal();
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
index 27d02e1..0a32e4a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt
@@ -20,9 +20,10 @@
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PackageParser
-import android.content.pm.parsing.AndroidPackage
+import android.platform.test.annotations.Presubmit
import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo
import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo
+import com.android.server.pm.parsing.pkg.AndroidPackage
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Test
@@ -36,6 +37,7 @@
* This test has to be updated manually whenever the info generation behavior changes, since
* there's no single place where flag -> field is defined besides this test.
*/
+@Presubmit
@RunWith(Parameterized::class)
class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
index 925af7f..191c038 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
@@ -17,6 +17,7 @@
package com.android.server.pm.parsing
import android.content.pm.PackageManager
+import android.platform.test.annotations.Presubmit
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Test
@@ -24,6 +25,7 @@
* Collects APKs from the device and verifies that the new parsing behavior outputs
* the same exposed Info object as the old parsing logic.
*/
+@Presubmit
class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index afd6e18..ca6b296 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -27,26 +27,27 @@
import android.content.pm.PackageUserState
import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo
-import android.content.pm.parsing.AndroidPackage
-import android.content.pm.parsing.PackageImpl
-import android.content.pm.parsing.PackageInfoUtils
import android.os.Debug
import android.os.Environment
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.om.mockThrowOnUnmocked
+import com.android.server.om.whenever
import com.android.server.pm.PackageManagerService
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.pkg.PackageStateUnserialized
import org.junit.BeforeClass
import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.mock
import java.io.File
open class AndroidPackageParsingTestBase {
companion object {
- /**
- * By default, don't parse all APKs on device, only the framework one.
- * Toggle this manually if working on package parsing.
- */
+ // TODO(chiuwinson): Enable in separate change to fail all presubmit builds and fix errors
private const val VERIFY_ALL_APKS = false
/** For auditing memory usage differences */
@@ -59,6 +60,11 @@
setCallback { true }
}
+ protected val packageParser2 = PackageParser2(null, false, context.resources.displayMetrics,
+ null, object : PackageParser2.Callback() {
+ override fun hasFeature(feature: String?) = true
+ })
+
/**
* It would be difficult to mock all possibilities, so just use the APKs on device.
* Unfortunately, this means the device must be bootable to verify potentially
@@ -80,9 +86,9 @@
.toList()
}
- private val dummyState = Mockito.mock(PackageUserState::class.java).apply {
+ private val dummyUserState = mock(PackageUserState::class.java).apply {
installed = true
- Mockito.`when`(isAvailable(Mockito.anyInt())).thenReturn(true)
+ Mockito.`when`(isAvailable(anyInt())).thenReturn(true)
}
lateinit var oldPackages: List<PackageParser.Package>
@@ -98,7 +104,7 @@
}
this.newPackages = apks.map {
- packageParser.parseParsedPackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
+ packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
}
if (DUMP_HPROF_TO_EXTERNAL) {
@@ -111,19 +117,27 @@
}
fun oldAppInfo(pkg: PackageParser.Package, flags: Int = 0): ApplicationInfo? {
- return PackageParser.generateApplicationInfo(pkg, flags, dummyState, 0)
+ return PackageParser.generateApplicationInfo(pkg, flags, dummyUserState, 0)
}
fun newAppInfo(pkg: AndroidPackage, flags: Int = 0): ApplicationInfo? {
- return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyState, 0)
+ return PackageInfoUtils.generateApplicationInfo(pkg, flags, dummyUserState, 0,
+ mockPkgSetting(pkg))
}
fun oldPackageInfo(pkg: PackageParser.Package, flags: Int = 0): PackageInfo? {
- return PackageParser.generatePackageInfo(pkg, intArrayOf(), flags, 5, 6, emptySet(), dummyState)
+ return PackageParser.generatePackageInfo(pkg, intArrayOf(), flags, 5, 6, emptySet(),
+ dummyUserState)
}
fun newPackageInfo(pkg: AndroidPackage, flags: Int = 0): PackageInfo? {
- return PackageInfoUtils.generate(pkg, intArrayOf(), flags, 5, 6, emptySet(), dummyState, 0)
+ return PackageInfoUtils.generate(pkg, intArrayOf(), flags, 5, 6, emptySet(),
+ dummyUserState, 0, mockPkgSetting(pkg))
+ }
+
+ private fun mockPkgSetting(aPkg: AndroidPackage) = mockThrowOnUnmocked<PackageSetting> {
+ this.pkg = aPkg
+ whenever(pkgState) { PackageStateUnserialized() }
}
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
similarity index 91%
rename from core/tests/coretests/src/android/content/pm/PackageParserTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
index 46e992e..66cd466 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/PackageParserLegacyCoreTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.content.pm;
+package com.android.server.pm.parsing;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -24,21 +24,26 @@
import android.apex.ApexInfo;
import android.content.Context;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ComponentParseUtils.ParsedComponent;
-import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
-import android.content.pm.parsing.ParsedPackage;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PermissionInfo;
+import android.content.pm.parsing.component.ParsedComponent;
+import android.content.pm.parsing.component.ParsedPermission;
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
-import android.util.Log;
+import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.frameworks.coretests.R;
+import com.android.frameworks.servicestests.R;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,9 +52,19 @@
import java.io.InputStream;
import java.util.function.Function;
+/**
+ * {@link ParsedPackage} was moved to the server, so this test moved along with it.
+ *
+ * This should be eventually refactored to a comprehensive parsing test, combined with its
+ * server variant in the parent package.
+ *
+ * TODO(b/135203078): Remove this test and replicate the cases in the actual com.android.server
+ * variant.
+ */
+@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class PackageParserTest {
+public class PackageParserLegacyCoreTest {
private static final String RELEASED = null;
private static final String OLDER_PRE_RELEASE = "A";
private static final String PRE_RELEASE = "B";
@@ -87,6 +102,10 @@
}
}
+ private PackageParser2 makeParser() {
+ return new PackageParser2(null, false, null, null, null);
+ }
+
@Test
public void testComputeMinSdkVersion_preReleasePlatform() {
// Do allow older release minSdkVersion on pre-release platform.
@@ -339,7 +358,7 @@
try {
outFile = copyRawResourceToFile(apkFileName, apkResourceId);
return converter.apply(
- new PackageParser().parseParsedPackage(outFile, 0 /* flags */, false));
+ makeParser().parsePackage(outFile, 0 /* flags */, false));
} finally {
if (outFile != null) {
outFile.delete();
@@ -350,9 +369,9 @@
/**
* Asserts basic properties about a component.
*/
- private void assertComponent(String className, int numIntents, ParsedComponent<?> component) {
- assertEquals(className, component.className);
- assertEquals(numIntents, component.intents.size());
+ private void assertComponent(String className, int numIntents, ParsedComponent component) {
+ assertEquals(className, component.getName());
+ assertEquals(numIntents, component.getIntents().size());
}
/**
@@ -406,7 +425,7 @@
@Test
public void testPackageWithComponents_cached() throws Exception {
checkPackageWithComponents(p ->
- PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p)));
+ PackageCacher.fromCacheEntryStatic(PackageCacher.toCacheEntryStatic(p)));
}
private void checkPackageWithComponents(
@@ -426,7 +445,7 @@
assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p);
- assertMetadata(p.getAppMetaData(),
+ assertMetadata(p.getMetaData(),
"key1", "value1",
"key2", "this_is_app");
assertMetadata(p.getActivities().get(0).getMetaData(),
@@ -448,7 +467,7 @@
// Hidden "app details" activity is added to every package.
boolean foundAppDetailsActivity = false;
for (int i = 0; i < ArrayUtils.size(p.getActivities()); i++) {
- if (p.getActivities().get(i).className.equals(
+ if (p.getActivities().get(i).getClassName().equals(
PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME)) {
foundAppDetailsActivity = true;
p.getActivities().remove(i);
@@ -466,7 +485,7 @@
@Test
public void testPackageWithIntentFilters_cached() throws Exception {
checkPackageWithIntentFilters(p ->
- PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p)));
+ PackageCacher.fromCacheEntryStatic(PackageCacher.toCacheEntryStatic(p)));
}
private void checkPackageWithIntentFilters(
@@ -474,20 +493,19 @@
ParsedPackage p = parsePackage(
"install_intent_filters.apk", R.raw.install_intent_filters,
converter);
- String packageName = "com.android.frameworks.coretests.install_intent_filters";
+ String packageName = "com.android.frameworks.servicestests.install_intent_filters";
assertEquals(packageName, p.getPackageName());
findAndRemoveAppDetailsActivity(p);
- Log.e("ParserTest", "" + p.getActivities());
assertEquals("Expected exactly one activity", 1, p.getActivities().size());
assertEquals("Expected exactly one intent filter",
- 1, p.getActivities().get(0).intents.size());
+ 1, p.getActivities().get(0).getIntents().size());
assertEquals("Expected exactly one mime group in intent filter",
- 1, p.getActivities().get(0).intents.get(0).countMimeGroups());
+ 1, p.getActivities().get(0).getIntents().get(0).countMimeGroups());
assertTrue("Did not find expected mime group 'mime_group_1'",
- p.getActivities().get(0).intents.get(0).hasMimeGroup("mime_group_1"));
+ p.getActivities().get(0).getIntents().get(0).hasMimeGroup("mime_group_1"));
}
@Test
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
similarity index 67%
rename from core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
index 21479c0..6b60042 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidHidlUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,25 +14,28 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_HIDL_MANAGER;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
- * Test for {@link AndroidHidlUpdater}
+ * Test for {@link com.android.server.pm.parsing.library.AndroidHidlUpdater}
*/
+@Presubmit
@SmallTest
@RunWith(JUnit4.class)
public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest {
@@ -41,13 +44,13 @@
@Test
public void targeted_at_P() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// no change, not system
@@ -56,17 +59,17 @@
@Test
public void targeted_at_P_system() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
- .hideAsParsed()
+ .hideAsParsed())
.setSystem(true);
// Should add both HIDL libraries
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(ANDROID_HIDL_MANAGER)
.addUsesLibrary(ANDROID_HIDL_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.setSystem(true)
.hideAsFinal();
@@ -75,15 +78,15 @@
@Test
public void targeted_at_P_not_empty_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// no change, not system
@@ -92,20 +95,20 @@
@Test
public void targeted_at_P_not_empty_usesLibraries_system() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.setSystem(true);
// The hidl jars should be added at the start of the list because it
// is not on the bootclasspath and the package targets pre-P.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(ANDROID_HIDL_MANAGER)
.addUsesLibrary(ANDROID_HIDL_BASE)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.setSystem(true)
.hideAsFinal();
@@ -114,15 +117,15 @@
@Test
public void targeted_at_P_in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(ANDROID_HIDL_MANAGER)
.addUsesLibrary(ANDROID_HIDL_BASE)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// Libraries are removed because they are not available for non-system apps
@@ -131,18 +134,18 @@
@Test
public void targeted_at_P_in_usesLibraries_system() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(ANDROID_HIDL_MANAGER)
.addUsesLibrary(ANDROID_HIDL_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.setSystem(true);
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.P)
.addUsesLibrary(ANDROID_HIDL_MANAGER)
.addUsesLibrary(ANDROID_HIDL_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.setSystem(true)
.hideAsFinal();
@@ -153,15 +156,15 @@
@Test
public void in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_HIDL_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// Dependency is removed, it is not available.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// Libraries are removed because they are not available for apps targeting Q+
@@ -170,15 +173,15 @@
@Test
public void in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ANDROID_HIDL_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// Dependency is removed, it is not available.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// Libraries are removed because they are not available for apps targeting Q+
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
similarity index 68%
rename from core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
index 65ae219..f536052 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestBaseUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,42 +14,44 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
-import android.content.pm.OptionalClassRunner;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test for {@link AndroidTestBaseUpdater}
*/
+@Presubmit
@SmallTest
@RunWith(OptionalClassRunner.class)
-@OptionalClassRunner.OptionalClass("android.content.pm.parsing.library.AndroidTestBaseUpdater")
+@OptionalClassRunner.OptionalClass("com.android.server.pm.parsing.library.AndroidTestBaseUpdater")
public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest {
private static final String OTHER_LIBRARY = "other.library";
@Test
public void targeted_at_Q() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
- .hideAsParsed();
+ .hideAsParsed());
// Should add org.apache.http.legacy.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -57,18 +59,18 @@
@Test
public void targeted_at_Q_not_empty_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed();
+ .hideAsParsed());
// The org.apache.http.legacy jar should be added at the start of the list because it
// is not on the bootclasspath and the package targets pre-Q.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary(ANDROID_TEST_BASE)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -76,15 +78,15 @@
@Test
public void targeted_at_Q_in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because although org.apache.http.legacy has been removed from
@@ -94,15 +96,15 @@
@Test
public void targeted_at_Q_in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.Q)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because although org.apache.http.legacy has been removed from
@@ -112,15 +114,15 @@
@Test
public void in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because the package explicitly requests org.apache.http.legacy
@@ -130,15 +132,15 @@
@Test
public void in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because the package explicitly requests org.apache.http.legacy
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
similarity index 68%
rename from core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
index 38755b9..77197e3 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -14,19 +14,21 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
-import android.content.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility.AndroidTestRunnerSplitUpdater;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -34,22 +36,23 @@
/**
* Test for {@link AndroidTestRunnerSplitUpdater}
*/
+@Presubmit
@SmallTest
@RunWith(JUnit4.class)
public class AndroidTestRunnerSplitUpdaterTest extends PackageSharedLibraryUpdaterTest {
@Test
public void android_test_runner_in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ANDROID_TEST_RUNNER)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ANDROID_TEST_MOCK)
.addUsesOptionalLibrary(ANDROID_TEST_RUNNER)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -57,17 +60,17 @@
@Test
public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_RUNNER)
.addUsesOptionalLibrary(ANDROID_TEST_MOCK)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_RUNNER)
.addUsesOptionalLibrary(ANDROID_TEST_MOCK)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
diff --git a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/OptionalClassRunner.java
similarity index 97%
rename from core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/OptionalClassRunner.java
index 05db8ee..0ebfe1a 100644
--- a/core/tests/coretests/src/android/content/pm/OptionalClassRunner.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/OptionalClassRunner.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.content.pm;
+package com.android.server.pm.parsing.library;
import org.junit.Assume;
import org.junit.runner.Description;
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
similarity index 68%
rename from core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
index 4c7899b..95b8d3f 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -14,42 +14,44 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-import android.content.pm.OptionalClassRunner;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Test;
import org.junit.runner.RunWith;
/**
- * Test for {@link OrgApacheHttpLegacyUpdater}
+ * Test for {@link com.android.server.pm.parsing.library.OrgApacheHttpLegacyUpdater}
*/
+@Presubmit
@SmallTest
@RunWith(OptionalClassRunner.class)
-@OptionalClassRunner.OptionalClass("android.content.pm.parsing.library.OrgApacheHttpLegacyUpdater")
+@OptionalClassRunner.OptionalClass("com.android.server.pm.parsing.library.OrgApacheHttpLegacyUpdater")
public class OrgApacheHttpLegacyUpdaterTest extends PackageSharedLibraryUpdaterTest {
private static final String OTHER_LIBRARY = "other.library";
@Test
public void targeted_at_O() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed();
+ .hideAsParsed());
// Should add org.apache.http.legacy.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -57,18 +59,18 @@
@Test
public void targeted_at_O_not_empty_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed();
+ .hideAsParsed());
// The org.apache.http.legacy jar should be added at the start of the list because it
// is not on the bootclasspath and the package targets pre-P.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -76,15 +78,15 @@
@Test
public void targeted_at_O_in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because although org.apache.http.legacy has been removed from
@@ -94,15 +96,15 @@
@Test
public void targeted_at_O_in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because although org.apache.http.legacy has been removed from
@@ -112,15 +114,15 @@
@Test
public void in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because the package explicitly requests org.apache.http.legacy
@@ -130,15 +132,15 @@
@Test
public void in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change is required because the package explicitly requests org.apache.http.legacy
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
similarity index 71%
rename from core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index 00d468d..ca38860 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -14,40 +14,43 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
-import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_MOCK;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
import android.content.pm.parsing.ParsingPackage;
-import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+@Presubmit
@SmallTest
@RunWith(JUnit4.class)
public class PackageBackwardCompatibilityTest extends PackageSharedLibraryUpdaterTest {
@Test
public void null_usesLibraries_and_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -74,12 +77,12 @@
*/
@Test
public void targeted_at_O() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed();
+ .hideAsParsed());
- ParsingPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.setTargetSdkVersion(Build.VERSION_CODES.O);
@@ -88,7 +91,7 @@
}
after.addUsesLibrary(ORG_APACHE_HTTP_LEGACY);
- checkBackwardsCompatibility(before, after.hideAsParsed().hideAsFinal());
+ checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
}
/**
@@ -105,16 +108,16 @@
+ ANDROID_TEST_BASE + " is on the bootclasspath",
PackageBackwardCompatibility.bootClassPathContainsATB());
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// android.test.base should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -129,12 +132,12 @@
*/
@Test
public void android_test_runner_in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_RUNNER)
- .hideAsParsed();
+ .hideAsParsed());
- ParsingPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
if (!PackageBackwardCompatibility.bootClassPathContainsATB()) {
after.addUsesLibrary(ANDROID_TEST_BASE);
@@ -142,7 +145,7 @@
after.addUsesLibrary(ANDROID_TEST_MOCK);
after.addUsesLibrary(ANDROID_TEST_RUNNER);
- checkBackwardsCompatibility(before, after.hideAsParsed().hideAsFinal());
+ checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
}
private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
similarity index 88%
rename from core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
index e7a80e1a..a71572f 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
import static org.junit.Assert.assertEquals;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.ParsedPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.util.function.Supplier;
@@ -32,7 +32,7 @@
static void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
- updaterSupplier.get().updatePackage(before);
+ updaterSupplier.get().updatePackage(before, false);
check(before.hideAsFinal(), after);
}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
similarity index 69%
rename from core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index fd3ba2b..1122490 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
-import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryAndroidTestBaseLibrary;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -33,6 +35,7 @@
/**
* Test for {@link RemoveUnnecessaryAndroidTestBaseLibrary}
*/
+@Presubmit
@SmallTest
@RunWith(JUnit4.class)
public class RemoveUnnecessaryAndroidTestBaseLibraryTest
@@ -42,13 +45,13 @@
@Test
public void targeted_at_O() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change required.
@@ -57,15 +60,15 @@
@Test
public void targeted_at_O_not_empty_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change required.
@@ -74,16 +77,16 @@
@Test
public void targeted_at_O_in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// android.test.base should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -91,16 +94,16 @@
@Test
public void targeted_at_O_in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// android.test.base should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -108,14 +111,14 @@
@Test
public void in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.addUsesLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// android.test.base should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
- .hideAsParsed()
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -123,16 +126,16 @@
@Test
public void in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// android.test.base should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -140,17 +143,17 @@
@Test
public void in_bothLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ANDROID_TEST_BASE)
.addUsesOptionalLibrary(ANDROID_TEST_BASE)
- .hideAsParsed();
+ .hideAsParsed());
// android.test.base should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
diff --git a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
similarity index 68%
rename from core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
rename to services/tests/servicestests/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index d3494d9..3cc8475 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-package android.content.pm.parsing.library;
+package com.android.server.pm.parsing.library;
-import static android.content.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
-import android.content.pm.parsing.AndroidPackage;
-import android.content.pm.parsing.PackageImpl;
-import android.content.pm.parsing.ParsedPackage;
-import android.content.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
import android.os.Build;
+import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
+import com.android.server.pm.parsing.library.PackageBackwardCompatibility.RemoveUnnecessaryOrgApacheHttpLegacyLibrary;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -33,6 +35,7 @@
/**
* Test for {@link RemoveUnnecessaryOrgApacheHttpLegacyLibrary}
*/
+@Presubmit
@SmallTest
@RunWith(JUnit4.class)
public class RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest
@@ -42,13 +45,13 @@
@Test
public void targeted_at_O() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change required.
@@ -57,15 +60,15 @@
@Test
public void targeted_at_O_not_empty_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed();
+ .hideAsParsed());
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(OTHER_LIBRARY)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
// No change required.
@@ -74,16 +77,16 @@
@Test
public void targeted_at_O_in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
// org.apache.http.legacy should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -91,16 +94,16 @@
@Test
public void targeted_at_O_in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
// org.apache.http.legacy should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -108,15 +111,16 @@
@Test
public void in_usesLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
// org.apache.http.legacy should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
- .hideAsParsed()
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -124,15 +128,16 @@
@Test
public void in_usesOptionalLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
// org.apache.http.legacy should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
- .hideAsParsed()
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
@@ -140,17 +145,17 @@
@Test
public void in_bothLibraries() {
- ParsedPackage before = PackageImpl.forParsing(PACKAGE_NAME)
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
.addUsesLibrary(ORG_APACHE_HTTP_LEGACY)
.addUsesOptionalLibrary(ORG_APACHE_HTTP_LEGACY)
- .hideAsParsed();
+ .hideAsParsed());
// org.apache.http.legacy should be removed from the libraries because it is provided
// on the bootclasspath and providing both increases start up cost unnecessarily.
- AndroidPackage after = PackageImpl.forParsing(PACKAGE_NAME)
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT)
- .hideAsParsed()
+ .hideAsParsed())
.hideAsFinal();
checkBackwardsCompatibility(before, after);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 9e57763..0fdffd5 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -66,6 +66,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
import android.view.Display;
@@ -83,6 +84,7 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -101,6 +103,8 @@
private static final int UID_EXEMPTED_1 = 10001;
private static final int USER_ID = 0;
private static final int USER_ID2 = 10;
+ private static final UserHandle USER_HANDLE_USER2 = new UserHandle(USER_ID2);
+ private static final int USER_ID3 = 11;
private static final String PACKAGE_UNKNOWN = "com.example.unknown";
@@ -150,6 +154,8 @@
boolean mDisplayOn;
DisplayManager.DisplayListener mDisplayListener;
String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
+ int[] mRunningUsers = new int[] {USER_ID};
+ List<UserHandle> mCrossProfileTargets = Collections.emptyList();
MyInjector(Context context, Looper looper) {
super(context, looper);
@@ -212,7 +218,7 @@
@Override
int[] getRunningUserIds() {
- return new int[] {USER_ID};
+ return mRunningUsers;
}
@Override
@@ -248,6 +254,11 @@
return false;
}
+ @Override
+ public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
+ return mCrossProfileTargets;
+ }
+
// Internal methods
void setDisplayOn(boolean on) {
@@ -379,10 +390,15 @@
}
private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
+ assertTimeout(controller, elapsedTime, bucket, USER_ID);
+ }
+
+ private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket,
+ int userId) {
mInjector.mElapsedRealtime = elapsedTime;
- controller.checkIdleStates(USER_ID);
+ controller.checkIdleStates(userId);
assertEquals(bucket,
- controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
+ controller.getAppStandbyBucket(PACKAGE_1, userId, mInjector.mElapsedRealtime,
false));
}
@@ -397,7 +413,11 @@
}
private int getStandbyBucket(AppStandbyController controller, String packageName) {
- return controller.getAppStandbyBucket(packageName, USER_ID, mInjector.mElapsedRealtime,
+ return getStandbyBucket(USER_ID, controller, packageName);
+ }
+
+ private int getStandbyBucket(int userId, AppStandbyController controller, String packageName) {
+ return controller.getAppStandbyBucket(packageName, userId, mInjector.mElapsedRealtime,
true);
}
@@ -1012,6 +1032,29 @@
assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
}
+ @Test
+ public void testUserInteraction_CrossProfile() throws Exception {
+ mInjector.mRunningUsers = new int[] {USER_ID, USER_ID2, USER_ID3};
+ mInjector.mCrossProfileTargets = Arrays.asList(USER_HANDLE_USER2);
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ assertEquals("Cross profile connected package bucket should be elevated on usage",
+ STANDBY_BUCKET_ACTIVE, getStandbyBucket(USER_ID2, mController, PACKAGE_1));
+ assertEquals("Not Cross profile connected package bucket should not be elevated on usage",
+ STANDBY_BUCKET_NEVER, getStandbyBucket(USER_ID3, mController, PACKAGE_1));
+
+ assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE, USER_ID);
+ assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE, USER_ID2);
+
+ assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET, USER_ID);
+ assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET, USER_ID2);
+
+ mInjector.mCrossProfileTargets = Collections.emptyList();
+ reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+ assertEquals("No longer cross profile connected package bucket should not be "
+ + "elevated on usage",
+ STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1));
+ }
+
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 3f0cda3..b7199f7 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -29,6 +29,7 @@
libs: [
"android.test.runner",
"android.test.base",
+ "android.test.mock",
],
dxflags: ["--multi-dex"],
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ccce043..d0283f7 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3246,7 +3246,7 @@
new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
false,
UserHandle.USER_ALL);
- verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class));
+ verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong());
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7f9732b..5829961 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -36,9 +36,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -67,6 +68,7 @@
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
+import android.test.mock.MockIContentProvider;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableContentResolver;
import android.util.ArrayMap;
@@ -87,6 +89,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
@@ -123,7 +126,7 @@
@Mock NotificationUsageStats mUsageStats;
@Mock RankingHandler mHandler;
@Mock PackageManager mPm;
- @Mock IContentProvider mTestIContentProvider;
+ @Spy IContentProvider mTestIContentProvider = new MockIContentProvider();
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@@ -170,12 +173,12 @@
when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI)))
- .thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(CANONICAL_SOUND_URI);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(SOUND_URI);
+ doReturn(CANONICAL_SOUND_URI)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
+ doReturn(CANONICAL_SOUND_URI)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(SOUND_URI)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
@@ -506,12 +509,13 @@
.appendQueryParameter("title", "Test")
.appendQueryParameter("canonical", "1")
.build();
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(canonicalBasedOnLocal);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(localUri);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(canonicalBasedOnLocal)))
- .thenReturn(localUri);
+ doReturn(canonicalBasedOnLocal)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(localUri)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(localUri)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(),
+ eq(canonicalBasedOnLocal));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -530,10 +534,10 @@
@Test
public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
Thread.sleep(3000);
- when(mTestIContentProvider.canonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(null);
- when(mTestIContentProvider.uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI)))
- .thenReturn(null);
+ doReturn(null)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
+ doReturn(null)
+ .when(mTestIContentProvider).uncanonicalize(any(), any(), eq(CANONICAL_SOUND_URI));
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -557,7 +561,8 @@
@Test
public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
// Not a local uncanonicalized uri, simulating that it fails to exist locally
- when(mTestIContentProvider.canonicalize(any(), any(), eq(SOUND_URI))).thenReturn(null);
+ doReturn(null)
+ .when(mTestIContentProvider).canonicalize(any(), any(), eq(SOUND_URI));
String id = "id";
String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 1dd0b1a..816e8e5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -22,6 +22,7 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
@@ -96,10 +97,33 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml_string.getBytes())), null);
- mSnoozeHelper.readXml(parser);
- assertTrue("Should read the notification time from xml and it should be more than zero",
- 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
- 0, "pkg", "key").doubleValue());
+ mSnoozeHelper.readXml(parser, 1);
+ assertEquals((long) Long.MAX_VALUE, (long) mSnoozeHelper
+ .getSnoozeTimeForUnpostedNotification(0, "pkg", "key"));
+ verify(mAm, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
+ }
+
+ @Test
+ public void testWriteXML_afterReading_noNPE()
+ throws XmlPullParserException, IOException {
+ final String max_time_str = Long.toString(Long.MAX_VALUE);
+ final String xml_string = "<snoozed-notifications>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key\" time=\"" + max_time_str + "\"/>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
+ + "</snoozed-notifications>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml_string.getBytes())), null);
+ mSnoozeHelper.readXml(parser, 1);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mSnoozeHelper.writeXml(serializer);
+ serializer.endDocument();
+ serializer.flush();
}
@Test
@@ -115,7 +139,7 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml_string.getBytes())), null);
- mSnoozeHelper.readXml(parser);
+ mSnoozeHelper.readXml(parser, 1);
assertEquals("Should read the notification context from xml and it should be `uri",
"uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification(
0, "pkg", "key"));
@@ -137,7 +161,7 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), "utf-8");
- mSnoozeHelper.readXml(parser);
+ mSnoozeHelper.readXml(parser, 1);
assertTrue("Should read the notification time from xml and it should be more than zero",
0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
0, "pkg", r.getKey()).doubleValue());
@@ -161,7 +185,7 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), "utf-8");
- mSnoozeHelper.readXml(parser);
+ mSnoozeHelper.readXml(parser, 2);
int systemUser = UserHandle.SYSTEM.getIdentifier();
assertTrue("Should see a past time returned",
System.currentTimeMillis() > mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
@@ -195,6 +219,30 @@
}
@Test
+ public void testScheduleRepostsForPersistedNotifications() throws Exception {
+ final String xml_string = "<snoozed-notifications>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key\" time=\"" + 10 + "\"/>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key2\" time=\"" + 15+ "\"/>"
+ + "</snoozed-notifications>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml_string.getBytes())), null);
+ mSnoozeHelper.readXml(parser, 4);
+
+ mSnoozeHelper.scheduleRepostsForPersistedNotifications(5);
+
+ ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq((long) 10), captor.capture());
+ assertEquals("key", captor.getValue().getIntent().getStringExtra(EXTRA_KEY));
+
+ ArgumentCaptor<PendingIntent> captor2 = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm).setExactAndAllowWhileIdle(anyInt(), eq((long) 15), captor2.capture());
+ assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY));
+ }
+
+ @Test
public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);
@@ -414,6 +462,23 @@
}
@Test
+ public void testGetSnoozedGroupNotifications_nonGrouped() throws Exception {
+ IntArray profileIds = new IntArray();
+ profileIds.add(UserHandle.USER_CURRENT);
+ when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+ NotificationRecord r = getNotificationRecord("pkg", 1, "tag",
+ UserHandle.CURRENT, "group", true);
+ NotificationRecord r2 = getNotificationRecord("pkg", 2, "tag",
+ UserHandle.CURRENT, null, true);
+ mSnoozeHelper.snooze(r, 1000);
+ mSnoozeHelper.snooze(r2, 1000);
+
+ assertEquals(1,
+ mSnoozeHelper.getNotifications("pkg", "group", UserHandle.USER_CURRENT).size());
+ // and no NPE
+ }
+
+ @Test
public void testGetSnoozedNotificationByKey() throws Exception {
IntArray profileIds = new IntArray();
profileIds.add(UserHandle.USER_CURRENT);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index bc2766c..cee486f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -16,6 +16,19 @@
package com.android.server.notification;
+import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
+import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
@@ -219,7 +232,7 @@
public void testZenOff_NoMuteApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_OFF;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
- Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
+ PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
doNothing().when(mZenModeHelperSpy).applyRestrictions(eq(false), anyBoolean(), anyInt());
@@ -230,10 +243,73 @@
}
@Test
+ public void testZenOn_NotificationApplied() {
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ // The most permissive policy
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
+ PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
+ | PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS
+ | PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REMINDERS
+ | PRIORITY_CATEGORY_REPEAT_CALLERS | PRIORITY_CATEGORY_SYSTEM, PRIORITY_SENDERS_ANY,
+ PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_ANYONE);
+ mZenModeHelperSpy.applyRestrictions();
+
+ doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyBoolean(), anyInt());
+ verify(mZenModeHelperSpy).applyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION);
+ verify(mZenModeHelperSpy).applyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_EVENT);
+ verify(mZenModeHelperSpy).applyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED);
+ verify(mZenModeHelperSpy).applyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT);
+ }
+
+ @Test
+ public void testZenOn_StarredCallers_CallTypesBlocked() {
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ // The most permissive policy
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
+ PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
+ | PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS
+ | PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REMINDERS
+ | PRIORITY_CATEGORY_SYSTEM,
+ PRIORITY_SENDERS_STARRED,
+ PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_ANYONE);
+ mZenModeHelperSpy.applyRestrictions();
+
+ doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyBoolean(), anyInt());
+ verify(mZenModeHelperSpy).applyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
+ verify(mZenModeHelperSpy).applyRestrictions(true, true,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
+ }
+
+ @Test
+ public void testZenOn_AllCallers_CallTypesAllowed() {
+ mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ // The most permissive policy
+ mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
+ PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_MESSAGES
+ | PRIORITY_CATEGORY_CONVERSATIONS | PRIORITY_CATEGORY_CALLS
+ | PRIORITY_CATEGORY_ALARMS | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REMINDERS
+ | PRIORITY_CATEGORY_REPEAT_CALLERS | PRIORITY_CATEGORY_SYSTEM,
+ PRIORITY_SENDERS_ANY,
+ PRIORITY_SENDERS_ANY, 0, CONVERSATION_SENDERS_ANYONE);
+ mZenModeHelperSpy.applyRestrictions();
+
+ doNothing().when(mZenModeHelperSpy).applyRestrictions(anyBoolean(), anyBoolean(), anyInt());
+ verify(mZenModeHelperSpy).applyRestrictions(true, false,
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
+ verify(mZenModeHelperSpy).applyRestrictions(true, false,
+ AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST);
+ }
+
+ @Test
public void testZenOn_AllowAlarmsMedia_NoAlarmMediaMuteApplied() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
- Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
+ PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true, false,
@@ -261,7 +337,7 @@
public void testTotalSilence() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
mZenModeHelperSpy.mConsolidatedPolicy = new Policy(Policy.PRIORITY_CATEGORY_ALARMS |
- Policy.PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
+ PRIORITY_CATEGORY_MEDIA, 0, 0, 0, 0, 0);
mZenModeHelperSpy.applyRestrictions();
// Total silence will silence alarms, media and system noises (but not vibrations)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index a0ea729..c9c3649 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1025,9 +1025,9 @@
public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
// Empty the home stack.
final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask();
- homeStack.forAllTasks((t) -> {
+ homeStack.forAllLeafTasks((t) -> {
homeStack.removeChild(t, "test");
- }, true /* traverseTopToBottom */, homeStack);
+ }, true /* traverseTopToBottom */);
mActivity.finishing = true;
doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities();
spyOn(mStack);
@@ -1051,9 +1051,9 @@
public void testCompleteFinishing_lastActivityAboveEmptyHomeStack() {
// Empty the home stack.
final ActivityStack homeStack = mActivity.getDisplay().getRootHomeTask();
- homeStack.forAllTasks((t) -> {
+ homeStack.forAllLeafTasks((t) -> {
homeStack.removeChild(t, "test");
- }, true /* traverseTopToBottom */, homeStack);
+ }, true /* traverseTopToBottom */);
mActivity.finishing = true;
spyOn(mStack);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1a8f2a6..b3c6b22 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -27,6 +27,7 @@
import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
+import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -998,12 +999,10 @@
public void testApplyTopFixedRotationTransform() {
mWm.mIsFixedRotationTransformEnabled = true;
final Configuration config90 = new Configuration();
- mDisplayContent.getDisplayRotation().setRotation(ROTATION_90);
- mDisplayContent.computeScreenConfiguration(config90);
- mDisplayContent.onRequestedOverrideConfigurationChanged(config90);
+ mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
final Configuration config = new Configuration();
- mDisplayContent.getDisplayRotation().setRotation(Surface.ROTATION_0);
+ mDisplayContent.getDisplayRotation().setRotation(ROTATION_0);
mDisplayContent.computeScreenConfiguration(config);
mDisplayContent.onRequestedOverrideConfigurationChanged(config);
@@ -1014,11 +1013,18 @@
app.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertTrue(app.isFixedRotationTransforming());
+ assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly(
+ ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
+ false /* forceUpdate */));
+ // The display should keep current orientation and the rotated configuration should apply
+ // to the activity.
assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation);
assertEquals(config90.orientation, app.getConfiguration().orientation);
+ assertEquals(config90.windowConfiguration.getBounds(), app.getBounds());
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
+ // The display should be rotated after the launch is finished.
assertFalse(app.hasFixedRotationTransform());
assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index ba57745..2b0ad89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -605,6 +605,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
addWindow(mWindow);
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -625,6 +626,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
addWindow(mWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index dd46673..ec20262 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -313,8 +313,8 @@
*/
@Test
public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect taskSize = new Rect(0, 0, 1000, 1000);
- final Rect stackSize = new Rect(0, 0, 300, 300);
+ final Rect configSize = new Rect(0, 0, 1000, 1000);
+ final Rect displayedSize = new Rect(0, 0, 300, 300);
// Create primary split-screen stack with a task.
final ActivityStack primaryStack = new StackBuilder(mRootWindowContainer)
@@ -325,11 +325,13 @@
final Task task = primaryStack.getTopMostTask();
// Resize dock stack.
- mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+ mService.resizeDockedStack(displayedSize, configSize, null, null, null);
// Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(stackSize, primaryStack.getBounds());
- assertEquals(taskSize, task.getBounds());
+ assertEquals(displayedSize, primaryStack.getDisplayedBounds());
+ assertEquals(displayedSize, primaryStack.getDisplayedBounds());
+ assertEquals(configSize, primaryStack.getBounds());
+ assertEquals(configSize, task.getBounds());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index cd53ece..45b51cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -28,6 +28,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -300,6 +301,7 @@
Rect newSize = new Rect(10, 10, 300, 300);
Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
c.windowConfiguration.setBounds(newSize);
+ doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
tile1.onRequestedOverrideConfigurationChanged(c);
assertEquals(newSize, stack.getBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 6e4be88..6387a3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -16,9 +16,11 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -33,6 +35,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations;
+import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -184,6 +187,16 @@
doReturn(stackOutset).when(stack).getStackOutset();
doReturn(true).when(stack).inMultiWindowMode();
+ // Mock the resolved override windowing mode to non-fullscreen
+ final WindowConfiguration windowConfiguration =
+ stack.getResolvedOverrideConfiguration().windowConfiguration;
+ spyOn(windowConfiguration);
+ doReturn(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
+ .when(windowConfiguration).getWindowingMode();
+
+ // Prevent adjust task dimensions
+ doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
+
final Rect stackBounds = new Rect(200, 200, 800, 1000);
// Update surface position and size by the given bounds.
stack.setBounds(stackBounds);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 18b640f..c3d3d83 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -86,6 +86,7 @@
public class StorageStatsService extends IStorageStatsManager.Stub {
private static final String TAG = "StorageStatsService";
+ private static final String PROP_STORAGE_CRATES = "fw.storage_crates";
private static final String PROP_DISABLE_QUOTA = "fw.disable_quota";
private static final String PROP_VERIFY_STORAGE = "fw.verify_storage";
@@ -595,6 +596,13 @@
Uri.parse("content://com.android.externalstorage.documents/"), null, false);
}
+ private static void checkCratesEnable() {
+ final boolean enable = SystemProperties.getBoolean(PROP_STORAGE_CRATES, false);
+ if (!enable) {
+ throw new IllegalStateException("Storage Crate feature is disabled.");
+ }
+ }
+
/**
* To enforce the calling or self to have the {@link android.Manifest.permission#MANAGE_CRATES}
* permission.
@@ -650,6 +658,7 @@
@Override
public ParceledListSlice<CrateInfo> queryCratesForPackage(String volumeUuid,
@NonNull String packageName, @UserIdInt int userId, @NonNull String callingPackage) {
+ checkCratesEnable();
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
@@ -677,6 +686,7 @@
@Override
public ParceledListSlice<CrateInfo> queryCratesForUid(String volumeUuid, int uid,
@NonNull String callingPackage) {
+ checkCratesEnable();
final int userId = UserHandle.getUserId(uid);
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
@@ -718,6 +728,7 @@
@Override
public ParceledListSlice<CrateInfo> queryCratesForUser(String volumeUuid, int userId,
@NonNull String callingPackage) {
+ checkCratesEnable();
if (userId != UserHandle.getCallingUserId()) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 6407ec7..84f411f 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
import android.debug.IAdbTransport;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
@@ -775,8 +776,10 @@
}
@Override
- public void onAdbEnabled(boolean enabled) {
- mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+ public void onAdbEnabled(boolean enabled, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+ }
}
}
@@ -1170,7 +1173,8 @@
}
protected boolean isAdbEnabled() {
- return LocalServices.getService(AdbManagerInternal.class).isAdbEnabled();
+ return LocalServices.getService(AdbManagerInternal.class)
+ .isAdbEnabled(AdbTransportType.USB);
}
protected void updateAdbNotification(boolean force) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
old mode 100644
new mode 100755
index 52213d8..c5fcf67
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -465,8 +465,27 @@
* @hide
*/
public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
+
+ /**
+ * When set for a call, indicates that this {@code Call} can be transferred to another
+ * number.
+ * Call supports the blind and assured call transfer feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER = 0x04000000;
+
+ /**
+ * When set for a call, indicates that this {@code Call} can be transferred to another
+ * ongoing call.
+ * Call supports the consultative call transfer feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x08000000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x04000000
+ // Next CAPABILITY value: 0x10000000
//******************************************************************************************
/**
@@ -699,6 +718,12 @@
if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) {
builder.append(" CAPABILITY_ADD_PARTICIPANT");
}
+ if (can(capabilities, CAPABILITY_TRANSFER)) {
+ builder.append(" CAPABILITY_TRANSFER");
+ }
+ if (can(capabilities, CAPABILITY_TRANSFER_CONSULTATIVE)) {
+ builder.append(" CAPABILITY_TRANSFER_CONSULTATIVE");
+ }
builder.append("]");
return builder.toString();
}
@@ -1564,6 +1589,30 @@
}
/**
+ * Instructs this {@code Call} to be transferred to another number.
+ *
+ * @param targetNumber The address to which the call will be transferred.
+ * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer,
+ * if {@code false}, it will initiate BLIND transfer.
+ *
+ * @hide
+ */
+ public void transfer(@NonNull Uri targetNumber, boolean isConfirmationRequired) {
+ mInCallAdapter.transferCall(mTelecomCallId, targetNumber, isConfirmationRequired);
+ }
+
+ /**
+ * Instructs this {@code Call} to be transferred to another ongoing call.
+ * This will initiate CONSULTATIVE transfer.
+ * @param toCall The other ongoing {@code Call} to which this call will be transferred.
+ *
+ * @hide
+ */
+ public void transfer(@NonNull android.telecom.Call toCall) {
+ mInCallAdapter.transferCall(mTelecomCallId, toCall.mTelecomCallId);
+ }
+
+ /**
* Instructs this {@code Call} to disconnect.
*/
public void disconnect() {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
old mode 100644
new mode 100755
index 3b0ba25..4604cd2
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -387,8 +387,25 @@
* @hide
*/
public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000;
+
+ /**
+ * Indicates that this {@code Connection} can be transferred to another
+ * number.
+ * Connection supports the blind and assured call transfer feature.
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER = 0x08000000;
+
+ /**
+ * Indicates that this {@code Connection} can be transferred to another
+ * ongoing {@code Connection}.
+ * Connection supports the consultative call transfer feature.
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x10000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x08000000
+ // Next CAPABILITY value: 0x20000000
//**********************************************************************************************
/**
@@ -967,6 +984,13 @@
if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) {
builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant");
}
+ if ((capabilities & CAPABILITY_TRANSFER) == CAPABILITY_TRANSFER) {
+ builder.append(isLong ? " CAPABILITY_TRANSFER" : " sup_trans");
+ }
+ if ((capabilities & CAPABILITY_TRANSFER_CONSULTATIVE)
+ == CAPABILITY_TRANSFER_CONSULTATIVE) {
+ builder.append(isLong ? " CAPABILITY_TRANSFER_CONSULTATIVE" : " sup_cTrans");
+ }
builder.append("]");
return builder.toString();
}
@@ -3092,6 +3116,26 @@
public void onReject(String replyMessage) {}
/**
+ * Notifies this Connection, a request to transfer to a target number.
+ * @param number the number to transfer this {@link Connection} to.
+ * @param isConfirmationRequired when {@code true}, the {@link ConnectionService}
+ * should wait until the transfer has successfully completed before disconnecting
+ * the current {@link Connection}.
+ * When {@code false}, the {@link ConnectionService} should signal the network to
+ * perform the transfer, but should immediately disconnect the call regardless of
+ * the outcome of the transfer.
+ * @hide
+ */
+ public void onTransfer(@NonNull Uri number, boolean isConfirmationRequired) {}
+
+ /**
+ * Notifies this Connection, a request to transfer to another Connection.
+ * @param otherConnection the {@link Connection} to transfer this call to.
+ * @hide
+ */
+ public void onTransfer(@NonNull Connection otherConnection) {}
+
+ /**
* Notifies this Connection of a request to silence the ringer.
* <p>
* The ringer may be silenced by any of the following methods:
@@ -3532,7 +3576,7 @@
* ATIS-1000082.
* @return the verification status.
*/
- public @VerificationStatus int getCallerNumberVerificationStatus() {
+ public final @VerificationStatus int getCallerNumberVerificationStatus() {
return mCallerNumberVerificationStatus;
}
@@ -3544,7 +3588,7 @@
* by
* {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}.
*/
- public void setCallerNumberVerificationStatus(
+ public final void setCallerNumberVerificationStatus(
@VerificationStatus int callerNumberVerificationStatus) {
mCallerNumberVerificationStatus = callerNumberVerificationStatus;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
old mode 100644
new mode 100755
index 2aea723..0dca006
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -128,6 +128,8 @@
private static final String SESSION_ANSWER = "CS.an";
private static final String SESSION_ANSWER_VIDEO = "CS.anV";
private static final String SESSION_DEFLECT = "CS.def";
+ private static final String SESSION_TRANSFER = "CS.trans";
+ private static final String SESSION_CONSULTATIVE_TRANSFER = "CS.cTrans";
private static final String SESSION_REJECT = "CS.r";
private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
private static final String SESSION_SILENCE = "CS.s";
@@ -196,6 +198,8 @@
private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
private static final int MSG_REJECT_WITH_REASON = 38;
private static final int MSG_ADD_PARTICIPANT = 39;
+ private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
+ private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
private static Connection sNullConnection;
@@ -481,6 +485,38 @@
}
@Override
+ public void transfer(@NonNull String callId, @NonNull Uri number,
+ boolean isConfirmationRequired, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_TRANSFER);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = number;
+ args.argi1 = isConfirmationRequired ? 1 : 0;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_EXPLICIT_CALL_TRANSFER, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void consultativeTransfer(@NonNull String callId, @NonNull String otherCallId,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CONSULTATIVE_TRANSFER);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = otherCallId;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(
+ MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void silence(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_SILENCE);
try {
@@ -1108,6 +1144,30 @@
}
break;
}
+ case MSG_EXPLICIT_CALL_TRANSFER: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_TRANSFER);
+ try {
+ final boolean isConfirmationRequired = args.argi1 == 1;
+ transfer((String) args.arg1, (Uri) args.arg2, isConfirmationRequired);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession(
+ (Session) args.arg3, SESSION_HANDLER + SESSION_CONSULTATIVE_TRANSFER);
+ try {
+ consultativeTransfer((String) args.arg1, (String) args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_DISCONNECT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT);
@@ -2042,6 +2102,18 @@
findConnectionForAction(callId, "reject").onReject(rejectReason);
}
+ private void transfer(String callId, Uri number, boolean isConfirmationRequired) {
+ Log.d(this, "transfer %s", callId);
+ findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired);
+ }
+
+ private void consultativeTransfer(String callId, String otherCallId) {
+ Log.d(this, "consultativeTransfer %s", callId);
+ Connection connection1 = findConnectionForAction(callId, "consultativeTransfer");
+ Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer");
+ connection1.onTransfer(connection2);
+ }
+
private void silence(String callId) {
Log.d(this, "silence %s", callId);
findConnectionForAction(callId, "silence").onSilence();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
old mode 100644
new mode 100755
index 9d291740..dd6c153
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.NonNull;
import android.bluetooth.BluetoothDevice;
import android.net.Uri;
import android.os.Bundle;
@@ -102,6 +103,35 @@
}
/**
+ * Instructs Telecom to transfer the specified call.
+ *
+ * @param callId The identifier of the call to transfer.
+ * @param targetNumber The address to transfer to.
+ * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer,
+ * if {@code false}, it will initiate BLIND transfer.
+ */
+ public void transferCall(@NonNull String callId, @NonNull Uri targetNumber,
+ boolean isConfirmationRequired) {
+ try {
+ mAdapter.transferCall(callId, targetNumber, isConfirmationRequired);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to transfer the specified call to another ongoing call.
+ *
+ * @param callId The identifier of the call to transfer.
+ * @param otherCallId The identifier of the other call to which this will be transferred.
+ */
+ public void transferCall(@NonNull String callId, @NonNull String otherCallId) {
+ try {
+ mAdapter.consultativeTransfer(callId, otherCallId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to disconnect the specified call.
*
* @param callId The identifier of the call to disconnect.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index a397d77..fb54179 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -81,6 +81,11 @@
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
+ void transfer(String callId, in Uri number, boolean isConfirmationRequired,
+ in Session.Info sessionInfo);
+
+ void consultativeTransfer(String callId, String otherCallId, in Session.Info sessionInfo);
+
void disconnect(String callId, in Session.Info sessionInfo);
void silence(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
old mode 100644
new mode 100755
index 9beff22..edf1cf4
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -36,6 +36,10 @@
void rejectCallWithReason(String callId, int rejectReason);
+ void transferCall(String callId, in Uri targetNumber, boolean isConfirmationRequired);
+
+ void consultativeTransfer(String callId, String otherCallId);
+
void disconnectCall(String callId);
void holdCall(String callId);
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index a27c480..9ae86c8 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -432,7 +432,7 @@
DataFailCause.LIMITED_TO_IPV6,
DataFailCause.VSNCP_TIMEOUT,
DataFailCause.VSNCP_GEN_ERROR,
- DataFailCause.VSNCP_APN_UNATHORIZED,
+ DataFailCause.VSNCP_APN_UNAUTHORIZED,
DataFailCause.VSNCP_PDN_LIMIT_EXCEEDED,
DataFailCause.VSNCP_NO_PDN_GATEWAY_ADDRESS,
DataFailCause.VSNCP_PDN_GATEWAY_UNREACHABLE,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9924839c..a7e52ea 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -36,6 +36,8 @@
import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
+import java.util.concurrent.TimeUnit;
+
/**
* Provides access to telephony configuration values that are carrier-specific.
*/
@@ -838,7 +840,8 @@
/**
* The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in
* Settings->More->Emergency broadcasts menu even though developer options is turned on.
- * @deprecated moved to cellbroadcastreceiver resource show_test_settings
+ * @deprecated Use {@code com.android.cellbroadcastreceiver.CellBroadcastReceiver} resource
+ * {@code show_test_settings} to control whether to show test alert settings or not.
*/
@Deprecated
public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
@@ -1987,6 +1990,13 @@
"carrier_allow_deflect_ims_call_bool";
/**
+ * Flag indicating whether the carrier supports explicit call transfer for an IMS call.
+ * @hide
+ */
+ public static final String KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL =
+ "carrier_allow_transfer_ims_call_bool";
+
+ /**
* Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
* been remotely held.
* <p>
@@ -2016,10 +2026,15 @@
"allow_add_call_during_video_call";
/**
- * When false, indicates that holding a video call is disabled
+ * When {@code true}, indicates that video calls can be put on hold in order to swap to another
+ * call (e.g. a new outgoing call).
+ * When {@code false}, indicates that video calls will be disconnected when swapping to another
+ * call.
+ * <p>
+ * This is {@code true} by default.
*/
- public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL =
- "allow_holding_video_call";
+ public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL =
+ "allow_hold_video_call_bool";
/**
* When true, indicates that the HD audio icon in the in-call screen should not be shown for
@@ -2466,6 +2481,21 @@
"parameters_use_for_5g_nr_signal_bar_int";
/**
+ * String array of default bandwidth values per network type.
+ * The entries should be of form "network_name:downstream,upstream", with values in Kbps.
+ * @hide
+ */
+ public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array";
+
+ /**
+ * For NR (non-standalone), whether to use the LTE value instead of NR value as the default for
+ * upstream bandwidth. Downstream bandwidth will still use the NR value as the default.
+ * @hide
+ */
+ public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL =
+ "bandwidth_nr_nsa_use_lte_value_for_upstream_bool";
+
+ /**
* Key identifying if voice call barring notification is required to be shown to the user.
* @hide
*/
@@ -2967,6 +2997,33 @@
"5g_icon_display_grace_period_sec_int";
/**
+ * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
+ */
+ public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_long";
+
+ /**
+ * Whether NR (non-standalone) should be unmetered for all frequencies.
+ * If either {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL} or
+ * {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL} are true, then this value will be ignored.
+ * @hide
+ */
+ public static final String KEY_UNMETERED_NR_NSA_BOOL = "unmetered_nr_nsa_bool";
+
+ /**
+ * Whether NR (non-standalone) frequencies above 6GHz (millimeter wave) should be unmetered.
+ * If this is true, then the value for {@link #KEY_UNMETERED_NR_NSA_BOOL} will be ignored.
+ * @hide
+ */
+ public static final String KEY_UNMETERED_NR_NSA_MMWAVE_BOOL = "unmetered_nr_nsa_mmwave_bool";
+
+ /**
+ * Whether NR (non-standalone) frequencies below 6GHz (sub6) should be unmetered.
+ * If this is true, then the value for {@link #KEY_UNMETERED_NR_NSA_BOOL} will be ignored.
+ * @hide
+ */
+ public static final String KEY_UNMETERED_NR_NSA_SUB6_BOOL = "unmetered_nr_nsa_sub6_bool";
+
+ /**
* Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
* this feature.
* @hide
@@ -3038,11 +3095,6 @@
"ping_test_before_data_switch_bool";
/**
- * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
- */
- public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
- "5g_watchdog_time_long";
- /**
* Controls whether to switch data to primary from opportunistic subscription
* if primary is out of service. This control only affects system or 1st party app
* initiated data switch, but will not override data switch initiated by privileged carrier apps
@@ -3505,6 +3557,7 @@
sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
sDefaults.putBoolean(KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL, false);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
@@ -3801,7 +3854,7 @@
sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
- sDefaults.putBoolean(KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
@@ -3917,6 +3970,13 @@
});
sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
CellSignalStrengthNr.USE_SSRSRP);
+ sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
+ "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA-IS95A:14,14", "CDMA-IS95B:14,14",
+ "1xRTT:30,30", "EvDo-rev.0:750,48", "EvDo-rev.A:950,550", "HSDPA:4300,620",
+ "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550:", "eHRPD:750,48",
+ "HSPAP:13000,3400", "TD-SCDMA:115,115", "LTE:30000,15000", "NR_NSA:47000,15000",
+ "NR_NSA_MMWAVE:145000,15000", "NR_SA:145000,15000"});
+ sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL, false);
sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
@@ -3932,6 +3992,11 @@
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:5G,connected:5G");
sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0);
+ /* Default value is 1 hour. */
+ sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
+ sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
+ sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
+ sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
@@ -3950,8 +4015,6 @@
/* Default value is 3 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_EXIT_HYSTERESIS_TIME_LONG, 3000);
sDefaults.putBoolean(KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL, true);
- /* Default value is 1 hour. */
- sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
sDefaults.putBoolean(KEY_SWITCH_DATA_TO_PRIMARY_IF_PRIMARY_IS_OOS_BOOL, true);
/* Default value is 60 seconds. */
sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG, 60000);
@@ -3993,7 +4056,7 @@
sDefaults.putAll(Wifi.getDefaults());
sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false);
- sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0);
+ sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1));
}
/**
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index e1c4bef..8b7a243 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -30,10 +30,8 @@
import java.util.Set;
/**
- * Returned as the reason for a data connection failure as defined by modem and some local errors.
- * @hide
+ * DataFailCause collects data connection failure causes code from different sources.
*/
-@SystemApi
public final class DataFailCause {
/** There is no failure */
public static final int NONE = 0;
@@ -841,8 +839,19 @@
/**
* Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
* configuration request because the requested APN is unauthorized.
+ *
+ * @deprecated Use {@link #VSNCP_APN_UNAUTHORIZED} instead.
+ *
+ * @hide
*/
- public static final int VSNCP_APN_UNATHORIZED = 0x8BE;
+ @SystemApi
+ @Deprecated
+ public static final int VSNCP_APN_UNATHORIZED = 0x8BE; // NOTYPO
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request because the requested APN is unauthorized.
+ */
+ public static final int VSNCP_APN_UNAUTHORIZED = 0x8BE;
/**
* Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
* configuration request because the PDN limit has been exceeded.
@@ -1318,6 +1327,7 @@
sFailCauseMap.put(VSNCP_TIMEOUT, "VSNCP_TIMEOUT");
sFailCauseMap.put(VSNCP_GEN_ERROR, "VSNCP_GEN_ERROR");
sFailCauseMap.put(VSNCP_APN_UNATHORIZED, "VSNCP_APN_UNATHORIZED");
+ sFailCauseMap.put(VSNCP_APN_UNAUTHORIZED, "VSNCP_APN_UNAUTHORIZED");
sFailCauseMap.put(VSNCP_PDN_LIMIT_EXCEEDED, "VSNCP_PDN_LIMIT_EXCEEDED");
sFailCauseMap.put(VSNCP_NO_PDN_GATEWAY_ADDRESS, "VSNCP_NO_PDN_GATEWAY_ADDRESS");
sFailCauseMap.put(VSNCP_PDN_GATEWAY_UNREACHABLE, "VSNCP_PDN_GATEWAY_UNREACHABLE");
@@ -1423,8 +1433,8 @@
if (configManager != null) {
PersistableBundle b = configManager.getConfigForSubId(subId);
if (b != null) {
- String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
- KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
+ String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager
+ .KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
if (permanentFailureStrings != null) {
permanentFailureSet = new HashSet<>();
for (Map.Entry<Integer, String> e : sFailCauseMap.entrySet()) {
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
new file mode 100644
index 0000000..c0833af
--- /dev/null
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -0,0 +1,109 @@
+/*
+ * 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.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information of a single logical modem indicating
+ * its id, supported rats and whether it supports voice or data, etc.
+ * @hide
+ */
+public class ModemInfo implements Parcelable {
+ public final int modemId;
+ public final int rat; /* bitset */
+ public final boolean isVoiceSupported;
+ public final boolean isDataSupported;
+
+ // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+ public ModemInfo(int modemId) {
+ this(modemId, 0, true, true);
+ }
+
+ public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
+ this.modemId = modemId;
+ this.rat = rat;
+ this.isVoiceSupported = isVoiceSupported;
+ this.isDataSupported = isDataSupported;
+ }
+
+ public ModemInfo(Parcel in) {
+ modemId = in.readInt();
+ rat = in.readInt();
+ isVoiceSupported = in.readBoolean();
+ isDataSupported = in.readBoolean();
+ }
+
+ @Override
+ public String toString() {
+ return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
+ + " isDataSupported:" + isDataSupported;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ ModemInfo s = (ModemInfo) o;
+
+ return (modemId == s.modemId
+ && rat == s.rat
+ && isVoiceSupported == s.isVoiceSupported
+ && isDataSupported == s.isDataSupported);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public @ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+ dest.writeInt(modemId);
+ dest.writeInt(rat);
+ dest.writeBoolean(isVoiceSupported);
+ dest.writeBoolean(isDataSupported);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
+ public ModemInfo createFromParcel(Parcel in) {
+ return new ModemInfo(in);
+ }
+
+ public ModemInfo[] newArray(int size) {
+ return new ModemInfo[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index a537928..6571858 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -16,20 +16,12 @@
package android.telephony;
-import android.annotation.LongDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
-import android.telephony.TelephonyManager.NetworkTypeBitMask;
-import com.android.internal.telephony.util.TelephonyUtils;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -38,365 +30,69 @@
* are shared between those modems defined by list of modem IDs.
*/
public final class PhoneCapability implements Parcelable {
- /** Modem feature indicating 3GPP2 capability. */
- public static final long MODEM_FEATURE_3GPP2_REG = 1 << 0;
- /** Modem feature indicating 3GPP capability. */
- public static final long MODEM_FEATURE_3GPP_REG = 1 << 1;
- /** Modem feature indicating CDMA 2000 with EHRPD capability. */
- public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 1 << 2;
- /** Modem feature indicating GSM capability. */
- public static final long MODEM_FEATURE_GERAN_REG = 1 << 3;
- /** Modem feature indicating UMTS capability. */
- public static final long MODEM_FEATURE_UTRAN_REG = 1 << 4;
- /** Modem feature indicating LTE capability. */
- public static final long MODEM_FEATURE_EUTRAN_REG = 1 << 5;
- /** Modem feature indicating 5G capability.*/
- public static final long MODEM_FEATURE_NGRAN_REG = 1 << 6;
- /** Modem feature indicating EN-DC capability. */
- public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 1 << 7;
- /** Modem feature indicating VoLTE capability (IMS registered). */
- public static final long MODEM_FEATURE_PS_VOICE_REG = 1 << 8;
- /** Modem feature indicating CS voice call capability. */
- public static final long MODEM_FEATURE_CS_VOICE_SESSION = 1 << 9;
- /** Modem feature indicating Internet connection capability. */
- public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1 << 10;
- /**
- * Modem feature indicating dedicated bearer capability.
- * For services that require a high level QoS (eg. VoLTE), the network can create
- * a dedicated bearer with the required QoS on top of an established default bearer.
- * This will provide a dedicated tunnel for one or more specific traffic types.
- */
- public static final long MODEM_FEATURE_DEDICATED_BEARER = 1 << 11;
- /** Modem feature indicating network scan capability. */
- public static final long MODEM_FEATURE_NETWORK_SCAN = 1 << 12;
- /** Modem feature indicating corresponding SIM has CDMA capability. */
- public static final long MODEM_FEATURE_CSIM = 1 << 13;
-
+ // Hardcoded default DSDS capability.
/** @hide */
- @LongDef(flag = true, prefix = {"MODEM_FEATURE_" }, value = {
- MODEM_FEATURE_3GPP2_REG,
- MODEM_FEATURE_3GPP_REG,
- MODEM_FEATURE_CDMA2000_EHRPD_REG,
- MODEM_FEATURE_GERAN_REG,
- MODEM_FEATURE_UTRAN_REG,
- MODEM_FEATURE_EUTRAN_REG,
- MODEM_FEATURE_NGRAN_REG,
- MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG,
- MODEM_FEATURE_PS_VOICE_REG,
- MODEM_FEATURE_CS_VOICE_SESSION,
- MODEM_FEATURE_INTERACTIVE_DATA_SESSION,
- MODEM_FEATURE_DEDICATED_BEARER,
- MODEM_FEATURE_NETWORK_SCAN,
- MODEM_FEATURE_CSIM,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ModemFeature {
- }
-
- /**
- * Hardcoded default DSDS capability.
- * @hide
- */
public static final PhoneCapability DEFAULT_DSDS_CAPABILITY;
- /**
- * Hardcoded default Single SIM single standby capability.
- * @hide
- */
+ // Hardcoded default Single SIM single standby capability.
+ /** @hide */
public static final PhoneCapability DEFAULT_SSSS_CAPABILITY;
static {
- List<List<Long>> capabilities = new ArrayList<>();
- List<Long> modem1 = new ArrayList<>();
- List<Long> modem2 = new ArrayList<>();
- modem1.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG
- | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_CS_VOICE_SESSION
- | MODEM_FEATURE_INTERACTIVE_DATA_SESSION | MODEM_FEATURE_DEDICATED_BEARER);
- modem2.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG
- | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_INTERACTIVE_DATA_SESSION
- | MODEM_FEATURE_DEDICATED_BEARER);
- capabilities.add(modem1);
- capabilities.add(modem2);
- List<String> uuids = new ArrayList<>();
- uuids.add("com.xxxx.lm0");
- uuids.add("com.xxxx.lm1");
- long rats = TelephonyManager.NETWORK_TYPE_BITMASK_GSM
- | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
- | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
- | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
- | TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
- DEFAULT_DSDS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null,
- uuids, null, capabilities);
+ ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true);
+ ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true);
- capabilities = new ArrayList<>();
- capabilities.add(modem1);
- uuids = new ArrayList<>();
- uuids.add("com.xxxx.lm0");
- DEFAULT_SSSS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null,
- uuids, null, capabilities);
+ List<ModemInfo> logicalModemList = new ArrayList<>();
+ logicalModemList.add(modemInfo1);
+ logicalModemList.add(modemInfo2);
+ DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
+
+ logicalModemList = new ArrayList<>();
+ logicalModemList.add(modemInfo1);
+ DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
}
- private final int mUtranUeCategoryDl;
- private final int mUtranUeCategoryUl;
- private final int mEutranUeCategoryDl;
- private final int mEutranUeCategoryUl;
- private final long mPsDataConnectionLingerTimeMillis;
- private final @NetworkTypeBitMask long mSupportedRats;
- private final List<Integer> mGeranBands;
- private final List<Integer> mUtranBands;
- private final List<Integer> mEutranBands;
- private final List<Integer> mNgranBands;
- private final List<String> mLogicalModemUuids;
- private final List<SimSlotCapability> mSimSlotCapabilities;
- private final @ModemFeature List<List<Long>> mConcurrentFeaturesSupport;
+ /** @hide */
+ public final int maxActiveVoiceCalls;
+ /** @hide */
+ public final int maxActiveData;
+ /** @hide */
+ public final int max5G;
+ /** @hide */
+ public final boolean validationBeforeSwitchSupported;
+ /** @hide */
+ public final List<ModemInfo> logicalModemList;
- /**
- * Default constructor to create a PhoneCapability object.
- * @param utranUeCategoryDl 3GPP UE category for UTRAN downlink.
- * @param utranUeCategoryUl 3GPP UE category for UTRAN uplink.
- * @param eutranUeCategoryDl 3GPP UE category for EUTRAN downlink.
- * @param eutranUeCategoryUl 3GPP UE category for EUTRAN uplink.
- * @param psDataConnectionLingerTimeMillis length of the grace period to allow a smooth
- * "handover" between data connections.
- * @param supportedRats all radio access technologies this phone is capable of supporting.
- * @param geranBands list of supported {@link AccessNetworkConstants.GeranBand}.
- * @param utranBands list of supported {@link AccessNetworkConstants.UtranBand}.
- * @param eutranBands list of supported {@link AccessNetworkConstants.EutranBand}.
- * @param ngranBands list of supported {@link AccessNetworkConstants.NgranBands}.
- * @param logicalModemUuids list of logical modem UUIDs, typically of the form
- * "com.xxxx.lmX", where X is the logical modem ID.
- * @param simSlotCapabilities list of {@link SimSlotCapability} for the device
- * @param concurrentFeaturesSupport list of list of concurrently supportable modem feature sets.
- * @hide
- */
- public PhoneCapability(int utranUeCategoryDl, int utranUeCategoryUl, int eutranUeCategoryDl,
- int eutranUeCategoryUl, long psDataConnectionLingerTimeMillis,
- @NetworkTypeBitMask long supportedRats, @Nullable List<Integer> geranBands,
- @Nullable List<Integer> utranBands, @Nullable List<Integer> eutranBands,
- @Nullable List<Integer> ngranBands, @Nullable List<String> logicalModemUuids,
- @Nullable List<SimSlotCapability> simSlotCapabilities,
- @Nullable @ModemFeature List<List<Long>> concurrentFeaturesSupport) {
- this.mUtranUeCategoryDl = utranUeCategoryDl;
- this.mUtranUeCategoryUl = utranUeCategoryUl;
- this.mEutranUeCategoryDl = eutranUeCategoryDl;
- this.mEutranUeCategoryUl = eutranUeCategoryUl;
- this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis;
- this.mSupportedRats = supportedRats;
- this.mGeranBands = TelephonyUtils.emptyIfNull(geranBands);
- this.mUtranBands = TelephonyUtils.emptyIfNull(utranBands);
- this.mEutranBands = TelephonyUtils.emptyIfNull(eutranBands);
- this.mNgranBands = TelephonyUtils.emptyIfNull(ngranBands);
- this.mLogicalModemUuids = TelephonyUtils.emptyIfNull(logicalModemUuids);
- this.mSimSlotCapabilities = TelephonyUtils.emptyIfNull(simSlotCapabilities);
- this.mConcurrentFeaturesSupport = TelephonyUtils.emptyIfNull(concurrentFeaturesSupport);
- }
-
- private PhoneCapability(Parcel in) {
- mUtranUeCategoryDl = in.readInt();
- mUtranUeCategoryUl = in.readInt();
- mEutranUeCategoryDl = in.readInt();
- mEutranUeCategoryUl = in.readInt();
- mPsDataConnectionLingerTimeMillis = in.readLong();
- mSupportedRats = in.readLong();
- mGeranBands = new ArrayList<>();
- in.readList(mGeranBands, Integer.class.getClassLoader());
- mUtranBands = new ArrayList<>();
- in.readList(mUtranBands, Integer.class.getClassLoader());
- mEutranBands = new ArrayList<>();
- in.readList(mEutranBands, Integer.class.getClassLoader());
- mNgranBands = new ArrayList<>();
- in.readList(mNgranBands, Integer.class.getClassLoader());
- mLogicalModemUuids = in.createStringArrayList();
- mSimSlotCapabilities = in.createTypedArrayList(SimSlotCapability.CREATOR);
- int length = in.readInt();
- mConcurrentFeaturesSupport = new ArrayList<>();
- for (int i = 0; i < length; i++) {
- ArrayList<Long> feature = new ArrayList<>();
- in.readList(feature, Long.class.getClassLoader());
- mConcurrentFeaturesSupport.add(feature);
- }
- }
-
- /**
- * 3GPP UE category for a given Radio Access Network and direction.
- *
- * References are:
- * TS 25.306 Table 4.1a EUTRAN downlink
- * TS 25.306 Table 5.1a-2 EUTRAN uplink
- * TS 25.306 Table 5.1a UTRAN downlink
- * TS 25.306 Table 5.1g UTRAN uplink
- *
- * @param uplink true for uplink direction and false for downlink direction.
- * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}.
- * @return the UE category, or -1 if it is not supported.
- */
- public int getUeCategory(boolean uplink, @RadioAccessNetworkType int accessNetworkType) {
- if (uplink) {
- switch (accessNetworkType) {
- case AccessNetworkType.UTRAN: return mUtranUeCategoryUl;
- case AccessNetworkType.EUTRAN: return mEutranUeCategoryUl;
- default: return -1;
- }
- } else {
- switch (accessNetworkType) {
- case AccessNetworkType.UTRAN: return mUtranUeCategoryDl;
- case AccessNetworkType.EUTRAN: return mEutranUeCategoryDl;
- default: return -1;
- }
- }
- }
-
- /**
- * In cellular devices that support a greater number of logical modems than
- * Internet connections, some devices support a grace period to allow a smooth "handover"
- * between those connections. If that feature is supported, then this API will provide
- * the length of that grace period in milliseconds. If it is not supported, the default value
- * for the grace period is 0.
- * @return handover linger time in milliseconds, or 0 if it is not supported.
- */
- public long getPsDataConnectionLingerTimeMillis() {
- return mPsDataConnectionLingerTimeMillis;
- }
-
- /**
- * The radio access technologies this device is capable of supporting.
- * @return a bitfield of all supported network types, defined in {@link TelephonyManager}
- */
- public @NetworkTypeBitMask long getSupportedRats() {
- return mSupportedRats;
- }
-
- /**
- * List of supported cellular bands for the given accessNetworkType.
- * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}.
- * @return a list of bands, or an empty list if the access network type is unsupported.
- */
- public @NonNull List<Integer> getBands(@RadioAccessNetworkType int accessNetworkType) {
- switch (accessNetworkType) {
- case AccessNetworkType.GERAN: return mGeranBands;
- case AccessNetworkType.UTRAN: return mUtranBands;
- case AccessNetworkType.EUTRAN: return mEutranBands;
- case AccessNetworkType.NGRAN: return mNgranBands;
- default: return new ArrayList<>();
- }
- }
-
- /**
- * List of logical modem UUIDs, each typically "com.xxxx.lmX", where X is the logical modem ID.
- * @return a list of modem UUIDs, one for every logical modem the device has.
- */
- public @NonNull List<String> getLogicalModemUuids() {
- return mLogicalModemUuids;
- }
-
- /**
- * List of {@link SimSlotCapability} for the device. The order of SIMs corresponds to the
- * order of modems in {@link #getLogicalModemUuids}.
- * @return a list of SIM slot capabilities, one for every SIM slot the device has.
- */
- public @NonNull List<SimSlotCapability> getSimSlotCapabilities() {
- return mSimSlotCapabilities;
- }
-
- /**
- * A List of Lists of concurrently supportable modem feature sets.
- *
- * Each entry in the top-level list is an independent configuration across all modems
- * that describes the capabilities of the device as a whole.
- *
- * Each entry in the second-level list is a bitfield of ModemFeatures that describes
- * the capabilities for a single modem. In the second-level list, the order of the modems
- * corresponds to order of the UUIDs in {@link #getLogicalModemUuids}.
- *
- * For symmetric capabilities that can only be active on one modem at a time, there will be
- * multiple configurations (equal to the number of modems) that shows it active on each modem.
- * For asymmetric capabilities that are only available on one of the modems, all configurations
- * will have that capability on just that one modem.
- *
- * The example below shows the concurrentFeaturesSupport for a 3-modem device with
- * theoretical capabilities SYMMETRIC (available on all modems, but only one at a time) and
- * ASYMMETRIC (only available on the first modem):
- * {
- * Configuration 1: ASYMMETRIC and SYMMETRIC on modem 1, modem 2 empty, modem 3 empty
- * {(ASYMMETRIC | SYMMETRIC), (), ()},
- *
- * Configuration 2: ASYMMETRIC on modem 1, SYMMETRIC on modem 2, modem 3 empty
- * {(ASYMMETRIC), (SYMMETRIC), ()},
- *
- * Configuration 3: ASYMMETRIC on modem 1, modem 2 empty, SYMMETRIC on modem 3
- * {(ASYMMETRIC), (), (SYMMETRIC)}
- * }
- *
- * @return List of all concurrently supportable modem features.
- */
- public @NonNull @ModemFeature List<List<Long>> getConcurrentFeaturesSupport() {
- return mConcurrentFeaturesSupport;
- }
-
- /**
- * How many modems can simultaneously have PS attached.
- * @return maximum number of active PS voice connections.
- */
- public int getMaxActivePsVoice() {
- return countFeature(MODEM_FEATURE_PS_VOICE_REG);
- }
-
- /**
- * How many modems can simultaneously support active data connections.
- * For DSDS, this will be 1, and for DSDA this will be 2.
- * @return maximum number of active Internet data sessions.
- */
- public int getMaxActiveInternetData() {
- return countFeature(MODEM_FEATURE_INTERACTIVE_DATA_SESSION);
- }
-
- /**
- * How many modems can simultaneously have dedicated bearer capability.
- * @return maximum number of active dedicated bearers.
- */
- public int getMaxActiveDedicatedBearers() {
- return countFeature(MODEM_FEATURE_DEDICATED_BEARER);
- }
-
- /**
- * Whether the CBRS band 48 is supported or not.
- * @return true if any RadioAccessNetwork supports CBRS and false if none do.
- * @hide
- */
- public boolean isCbrsSupported() {
- return mEutranBands.contains(AccessNetworkConstants.EutranBand.BAND_48)
- || mNgranBands.contains(AccessNetworkConstants.NgranBands.BAND_48);
- }
-
- private int countFeature(@ModemFeature long feature) {
- int count = 0;
- for (long featureSet : mConcurrentFeaturesSupport.get(0)) {
- if ((featureSet & feature) != 0) {
- count++;
- }
- }
- return count;
+ /** @hide */
+ public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
+ List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
+ this.maxActiveVoiceCalls = maxActiveVoiceCalls;
+ this.maxActiveData = maxActiveData;
+ this.max5G = max5G;
+ // Make sure it's not null.
+ this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+ this.validationBeforeSwitchSupported = validationBeforeSwitchSupported;
}
@Override
public String toString() {
- return "utranUeCategoryDl=" + mUtranUeCategoryDl
- + " utranUeCategoryUl=" + mUtranUeCategoryUl
- + " eutranUeCategoryDl=" + mEutranUeCategoryDl
- + " eutranUeCategoryUl=" + mEutranUeCategoryUl
- + " psDataConnectionLingerTimeMillis=" + mPsDataConnectionLingerTimeMillis
- + " supportedRats=" + mSupportedRats + " geranBands=" + mGeranBands
- + " utranBands=" + mUtranBands + " eutranBands=" + mEutranBands
- + " ngranBands=" + mNgranBands + " logicalModemUuids=" + mLogicalModemUuids
- + " simSlotCapabilities=" + mSimSlotCapabilities
- + " concurrentFeaturesSupport=" + mConcurrentFeaturesSupport;
+ return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData
+ + " max5G=" + max5G + "logicalModemList:"
+ + Arrays.toString(logicalModemList.toArray());
+ }
+
+ private PhoneCapability(Parcel in) {
+ maxActiveVoiceCalls = in.readInt();
+ maxActiveData = in.readInt();
+ max5G = in.readInt();
+ validationBeforeSwitchSupported = in.readBoolean();
+ logicalModemList = new ArrayList<>();
+ in.readList(logicalModemList, ModemInfo.class.getClassLoader());
}
@Override
public int hashCode() {
- return Objects.hash(mUtranUeCategoryDl, mUtranUeCategoryUl, mEutranUeCategoryDl,
- mEutranUeCategoryUl, mPsDataConnectionLingerTimeMillis, mSupportedRats, mGeranBands,
- mUtranBands, mEutranBands, mNgranBands, mLogicalModemUuids, mSimSlotCapabilities,
- mConcurrentFeaturesSupport);
+ return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
+ validationBeforeSwitchSupported);
}
@Override
@@ -411,19 +107,11 @@
PhoneCapability s = (PhoneCapability) o;
- return (mUtranUeCategoryDl == s.mUtranUeCategoryDl
- && mUtranUeCategoryUl == s.mUtranUeCategoryUl
- && mEutranUeCategoryDl == s.mEutranUeCategoryDl
- && mEutranUeCategoryUl == s.mEutranUeCategoryUl
- && mPsDataConnectionLingerTimeMillis == s.mPsDataConnectionLingerTimeMillis
- && mSupportedRats == s.mSupportedRats
- && mGeranBands.equals(s.mGeranBands)
- && mUtranBands.equals(s.mUtranBands)
- && mEutranBands.equals(s.mEutranBands)
- && mNgranBands.equals(s.mNgranBands)
- && mLogicalModemUuids.equals(s.mLogicalModemUuids)
- && mSimSlotCapabilities.equals(s.mSimSlotCapabilities)
- && mConcurrentFeaturesSupport.equals(s.mConcurrentFeaturesSupport));
+ return (maxActiveVoiceCalls == s.maxActiveVoiceCalls
+ && maxActiveData == s.maxActiveData
+ && max5G == s.max5G
+ && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported
+ && logicalModemList.equals(s.logicalModemList));
}
/**
@@ -436,33 +124,21 @@
/**
* {@link Parcelable#writeToParcel}
*/
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mUtranUeCategoryDl);
- dest.writeInt(mUtranUeCategoryUl);
- dest.writeInt(mEutranUeCategoryDl);
- dest.writeInt(mEutranUeCategoryUl);
- dest.writeLong(mPsDataConnectionLingerTimeMillis);
- dest.writeLong(mSupportedRats);
- dest.writeList(mGeranBands);
- dest.writeList(mUtranBands);
- dest.writeList(mEutranBands);
- dest.writeList(mNgranBands);
- dest.writeStringList(mLogicalModemUuids);
- dest.writeTypedList(mSimSlotCapabilities);
- dest.writeInt(mConcurrentFeaturesSupport.size());
- for (List<Long> feature : mConcurrentFeaturesSupport) {
- dest.writeList(feature);
- }
+ public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
+ dest.writeInt(maxActiveVoiceCalls);
+ dest.writeInt(maxActiveData);
+ dest.writeInt(max5G);
+ dest.writeBoolean(validationBeforeSwitchSupported);
+ dest.writeList(logicalModemList);
}
- public static final @NonNull Parcelable.Creator<PhoneCapability> CREATOR =
- new Parcelable.Creator() {
- public PhoneCapability createFromParcel(Parcel in) {
- return new PhoneCapability(in);
- }
+ public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() {
+ public PhoneCapability createFromParcel(Parcel in) {
+ return new PhoneCapability(in);
+ }
- public PhoneCapability[] newArray(int size) {
- return new PhoneCapability[size];
- }
- };
+ public PhoneCapability[] newArray(int size) {
+ return new PhoneCapability[size];
+ }
+ };
}
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 708adeb..e37a9b9 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -257,8 +257,7 @@
* Return the cause code for the most recent change in {@link #getState}. In the event of an
* error, this cause code will be non-zero.
*/
- // FIXME(b144774287): some of these cause codes should have a prescribed meaning.
- public int getLastCauseCode() {
+ public @DataFailureCause int getLastCauseCode() {
return mFailCause;
}
diff --git a/telephony/java/android/telephony/SimSlotCapability.java b/telephony/java/android/telephony/SimSlotCapability.java
deleted file mode 100644
index b4fef46..0000000
--- a/telephony/java/android/telephony/SimSlotCapability.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Capabilities for a SIM Slot.
- */
-public final class SimSlotCapability implements Parcelable {
- /** Slot type for UICC (removable SIM). */
- public static final int SLOT_TYPE_UICC = 1;
- /** Slot type for iUICC/iSIM (integrated SIM). */
- public static final int SLOT_TYPE_IUICC = 2;
- /** Slot type for eUICC/eSIM (embedded SIM). */
- public static final int SLOT_TYPE_EUICC = 3;
- /** Slot type for soft SIM (no physical SIM). */
- public static final int SLOT_TYPE_SOFT_SIM = 4;
-
- /** @hide */
- @IntDef(prefix = {"SLOT_TYPE_" }, value = {
- SLOT_TYPE_UICC,
- SLOT_TYPE_IUICC,
- SLOT_TYPE_EUICC,
- SLOT_TYPE_SOFT_SIM,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SlotType {
- }
-
- private final int mPhysicalSlotIndex;
- private final int mSlotType;
-
- /** @hide */
- public SimSlotCapability(int physicalSlotId, int slotType) {
- this.mPhysicalSlotIndex = physicalSlotId;
- this.mSlotType = slotType;
- }
-
- private SimSlotCapability(Parcel in) {
- mPhysicalSlotIndex = in.readInt();
- mSlotType = in.readInt();
- }
-
- /**
- * @return physical SIM slot index
- */
- public int getPhysicalSlotIndex() {
- return mPhysicalSlotIndex;
- }
-
- /**
- * @return type of SIM {@link SlotType}
- */
- public @SlotType int getSlotType() {
- return mSlotType;
- }
-
- @Override
- public String toString() {
- return "mPhysicalSlotIndex=" + mPhysicalSlotIndex + " slotType=" + mSlotType;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPhysicalSlotIndex, mSlotType);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null || !(o instanceof SimSlotCapability) || hashCode() != o.hashCode()) {
- return false;
- }
-
- if (this == o) {
- return true;
- }
-
- SimSlotCapability s = (SimSlotCapability) o;
-
- return (mPhysicalSlotIndex == s.mPhysicalSlotIndex && mSlotType == s.mSlotType);
- }
-
- /**
- * {@link Parcelable#describeContents}
- */
- public int describeContents() {
- return 0;
- }
-
- /**
- * {@link Parcelable#writeToParcel}
- */
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mPhysicalSlotIndex);
- dest.writeInt(mSlotType);
- }
-
- public static final @NonNull Parcelable.Creator<SimSlotCapability> CREATOR =
- new Parcelable.Creator() {
- public SimSlotCapability createFromParcel(Parcel in) {
- return new SimSlotCapability(in);
- }
-
- public SimSlotCapability[] newArray(int size) {
- return new SimSlotCapability[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6c920f1..8479db6 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2094,8 +2094,12 @@
/**
* Gets the total capacity of SMS storage on RUIM and SIM cards
+ * <p>
+ * This is the number of 176 byte EF-SMS records which can be stored on the RUIM or SIM card.
+ * <p>
+ * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information
*
- * @return the total capacity count of SMS on RUIM and SIM cards
+ * @return the total number of SMS records which can be stored on the RUIM or SIM cards.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 832771d..336aa0e 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -217,6 +217,20 @@
private boolean mAreUiccApplicationsEnabled = true;
/**
+ * Public copy constructor.
+ * @hide
+ */
+ public SubscriptionInfo(SubscriptionInfo info) {
+ this(info.mId, info.mIccId, info.mSimSlotIndex, info.mDisplayName, info.mCarrierName,
+ info.mNameSource, info.mIconTint, info.mNumber, info.mDataRoaming, info.mIconBitmap,
+ info.mMcc, info.mMnc, info.mCountryIso, info.mIsEmbedded, info.mNativeAccessRules,
+ info.mCardString, info.mCardId, info.mIsOpportunistic,
+ info.mGroupUUID == null ? null : info.mGroupUUID.toString(), info.mIsGroupDisabled,
+ info.mCarrierId, info.mProfileClass, info.mSubscriptionType, info.mGroupOwner,
+ info.mCarrierConfigAccessRules, info.mAreUiccApplicationsEnabled);
+ }
+
+ /**
* @hide
*/
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -291,13 +305,27 @@
}
/**
- * @return the ICC ID.
+ * Returns the ICC ID if the calling app has been granted the READ_PRIVILEGED_PHONE_STATE
+ * permission, has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or
+ * is a device owner or profile owner that has been granted the READ_PHONE_STATE permission.
+ * The profile owner is an app that owns a managed profile on the device; for more details see
+ * <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
+ * owner access is deprecated and will be removed in a future release.
+ *
+ * @return the ICC ID, or an empty string if one of these requirements is not met
*/
public String getIccId() {
return this.mIccId;
}
/**
+ * @hide
+ */
+ public void clearIccId() {
+ this.mIccId = "";
+ }
+
+ /**
* @return the slot index of this Subscription's SIM card.
*/
public int getSimSlotIndex() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 68b6683..0660776 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -47,7 +47,6 @@
import android.content.Intent;
import android.database.Cursor;
import android.net.ConnectivityManager;
-import android.net.NetworkStats;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -1877,24 +1876,6 @@
//
/**
- * Returns the {@link PhoneCapability} for the device or null if it is not available.
- * <p>
- * Requires Permission: READ_PHONE_STATE or that the calling app has
- * carrier privileges (see {@link #hasCarrierPrivileges}).
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @Nullable
- public PhoneCapability getPhoneCapability() {
- try {
- ITelephony telephony = getITelephony();
- return telephony == null ? null :
- telephony.getPhoneCapability(getSubId(), getOpPackageName(), getFeatureId());
- } catch (RemoteException ex) {
- return null;
- }
- }
-
- /**
* Returns the software version number for the device, for example,
* the IMEI/SV for GSM phones. Return null if the software version is
* not available.
@@ -6180,9 +6161,11 @@
* @param AID Application id. See ETSI 102.221 and 101.220.
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
return iccOpenLogicalChannel(getSubId(), AID, p2);
@@ -6214,9 +6197,11 @@
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
try {
@@ -6245,9 +6230,9 @@
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#close()}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@@ -6275,9 +6260,9 @@
* @param channel is the channel id to be closed as returned by a successful
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#close()}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public boolean iccCloseLogicalChannel(int channel) {
return iccCloseLogicalChannel(getSubId(), channel);
@@ -6297,9 +6282,9 @@
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#close()}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public boolean iccCloseLogicalChannel(int subId, int channel) {
try {
@@ -6336,9 +6321,9 @@
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@@ -6377,9 +6362,9 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduLogicalChannel(int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6409,9 +6394,9 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6448,9 +6433,12 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@@ -6487,9 +6475,12 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduBasicChannel(int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6517,9 +6508,12 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduBasicChannel(int subId, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6548,9 +6542,12 @@
* @param p3 P3 value of the APDU command.
* @param filePath
* @return The APDU response.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
String filePath) {
@@ -6573,9 +6570,12 @@
* @param filePath
* @return The APDU response.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
int p3, String filePath) {
@@ -6602,9 +6602,12 @@
* @return The APDU response from the ICC card in hexadecimal format
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String sendEnvelopeWithStatus(String content) {
return sendEnvelopeWithStatus(getSubId(), content);
@@ -6625,9 +6628,12 @@
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String sendEnvelopeWithStatus(int subId, String content) {
try {
@@ -11082,28 +11088,6 @@
}
/**
- * Get aggregated video call data usage since boot.
- * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
- *
- * @param how one of the NetworkStats.STATS_PER_* constants depending on whether the request is
- * for data usage per uid or overall usage.
- * @return Snapshot of video call data usage
- * @hide
- */
- public NetworkStats getVtDataUsage(int how) {
- boolean perUidStats = (how == NetworkStats.STATS_PER_UID);
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getVtDataUsage(getSubId(), perUidStats);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getVtDataUsage", e);
- }
- return null;
- }
-
- /**
* Policy control of data connection. Usually used when data limit is passed.
* @param enabled True if enabling the data, otherwise disabling.
* @hide
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
old mode 100644
new mode 100755
index 1b583fd..80c38cb
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -16,6 +16,8 @@
package android.telephony.ims;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.CallQuality;
@@ -451,6 +453,21 @@
}
/**
+ * Received success response for call transfer request.
+ */
+ public void callSessionTransferred(@NonNull ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Received failure response for call transfer request.
+ */
+ public void callSessionTransferFailed(@NonNull ImsCallSession session,
+ @Nullable ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
* Called when the IMS service reports a change to the call quality.
*/
public void callQualityChanged(CallQuality callQuality) {
@@ -795,6 +812,41 @@
}
/**
+ * Transfers an ongoing call.
+ *
+ * @param number number to be transferred to.
+ * @param isConfirmationRequired indicates blind or assured transfer.
+ */
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.transfer(number, isConfirmationRequired);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Transfers a call to another ongoing call.
+ *
+ * @param transferToSession the other ImsCallSession to which this session will be transferred.
+ */
+ public void transfer(@NonNull ImsCallSession transferToSession) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ if (transferToSession != null) {
+ miSession.consultativeTransfer(transferToSession.getSession());
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Terminates a call.
*
* @see Listener#callSessionTerminated
@@ -1410,6 +1462,20 @@
}
}
+ @Override
+ public void callSessionTransferred() {
+ if (mListener != null) {
+ mListener.callSessionTransferred(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
/**
* Call quality updated
*/
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index cc2ebb9..36d2067 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -148,6 +148,12 @@
void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
/**
+ * Notifies the result of transfer request.
+ */
+ void callSessionTransferred();
+ void callSessionTransferFailed(in ImsReasonInfo reasonInfo);
+
+ /**
* Notifies of a change to the call quality.
* @param callQuality then updated call quality
*/
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
old mode 100644
new mode 100755
index 75bd6a7..06aa642
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,8 @@
package android.telephony.ims.compat.stub;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Message;
import android.os.RemoteException;
@@ -197,6 +199,29 @@
}
/**
+ * Transfer an established call to given number, disconnecting the ongoing call
+ * when the transfer is complete.
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired when {@code true}, then the {@link ImsCallSessionImplBase}
+ * should wait until the transfer has successfully completed before disconnecting the current
+ * {@link ImsCallSessionImplBase}. When {@code false}, the {@link ImsCallSessionImplBase}
+ * should signal the network to perform the transfer, but should immediately disconnect the
+ * call regardless of the outcome of the transfer.
+ */
+ @Override
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ }
+
+ /**
+ * Transfer an established call to an existing ongoing session.
+ * When the transfer is complete, the current call gets disconnected locally.
+ */
+ @Override
+ public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
@@ -610,6 +635,17 @@
}
@Override
+ public void callSessionTransferred() throws RemoteException {
+ mNewListener.callSessionTransferred();
+ }
+
+ @Override
+ public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionTransferFailed(reasonInfo);
+ }
+
+ @Override
public void callQualityChanged(CallQuality callQuality) throws RemoteException {
mNewListener.callQualityChanged(callQuality);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index e8f69ea..73ba0e3 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,7 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Message;
@@ -183,6 +184,18 @@
}
@Override
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+ }
+
+ @Override
+ public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+ ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+ otherSession.setServiceImpl(transferToSession);
+ ImsCallSessionImplBase.this.transfer(otherSession);
+ }
+
+ @Override
public void terminate(int reason) {
ImsCallSessionImplBase.this.terminate(reason);
}
@@ -423,6 +436,26 @@
}
/**
+ * Transfer an established call to given number
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired if {@code True}, indicates Assured transfer,
+ * if {@code False} it indicates Blind transfer.
+ * @hide
+ */
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ }
+
+ /**
+ * Transfer an established call to another call session
+ *
+ * @param otherSession The other ImsCallSession to transfer the ongoing session to.
+ * @hide
+ */
+ public void transfer(@NonNull ImsCallSessionImplBase otherSession) {
+ }
+
+ /**
* Terminates a call.
*
* @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 15234e5..0466efc 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -18,7 +18,6 @@
import android.os.Message;
import android.telephony.ims.aidl.IImsCallSessionListener;
-
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsStreamMediaProfile;
import com.android.ims.internal.IImsVideoCallProvider;
@@ -151,6 +150,22 @@
void reject(int reason);
/**
+ * Transfer an established call to given number
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired if {@code True}, indicates Assured transfer,
+ * if {@code False} it indicates Blind transfer.
+ */
+ void transfer(String number, boolean isConfirmationRequired);
+
+ /**
+ * Transfer an established call to another call session
+ *
+ * @param transferToSession The other ImsCallSession to transfer the ongoing session to.
+ */
+ void consultativeTransfer(in IImsCallSession transferToSession);
+
+ /**
* Terminates a call.
*
* @see Listener#callSessionTerminated
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index b33a9f1..1c62cc4 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -184,6 +184,12 @@
void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
/**
+ * Notifies about the response for call transfer request.
+ */
+ void callSessionTransferred();
+
+ void callSessionTransferFailed(in ImsReasonInfo reasonInfo);
+ /**
* Notifies of a change to the call quality.
* @param callQuality then updated call quality
*/
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 1449a62..f61d4e1 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -114,6 +114,7 @@
public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
+ public static final int EVENT_UPDATE_CARRIER_CONFIGS = BASE + 55;
/***** Constants *****/
@@ -123,4 +124,6 @@
public static final String APN_TYPE_KEY = "apnType";
public static final String PROVISIONING_URL_KEY = "provisioningUrl";
+ public static final String BANDWIDTH_SOURCE_MODEM_KEY = "modem";
+ public static final String BANDWIDTH_SOURCE_CARRIER_CONFIG_KEY = "carrier_config";
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 168c8b6..861925f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -39,7 +39,6 @@
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
-import android.telephony.PhoneCapability;
import android.telephony.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.RadioAccessSpecifier;
@@ -1624,16 +1623,6 @@
void carrierActionResetAll(int subId);
/**
- * Get aggregated video call data usage since boot.
- * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
- *
- * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
- * @return Snapshot of video call data usage
- * @hide
- */
- NetworkStats getVtDataUsage(int subId, boolean perUidStats);
-
- /**
* Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
* reason.
*
@@ -1899,17 +1888,12 @@
/**
* Return the network selection mode on the subscription with id {@code subId}.
*/
- int getNetworkSelectionMode(int subId);
+ int getNetworkSelectionMode(int subId);
- /**
- * Return the PhoneCapability for the device.
- */
- PhoneCapability getPhoneCapability(int subId, String callingPackage, String callingFeatureId);
-
- /**
+ /**
* Return true if the device is in emergency sms mode, false otherwise.
*/
- boolean isInEmergencySmsMode();
+ boolean isInEmergencySmsMode();
/**
* Return the modem radio power state for slot index.
diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java
index f7ec11c..d1d64d3 100644
--- a/test-mock/src/android/test/mock/MockContentProvider.java
+++ b/test-mock/src/android/test/mock/MockContentProvider.java
@@ -156,6 +156,12 @@
}
@Override
+ public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ RemoteCallback callback) {
+ MockContentProvider.this.canonicalizeAsync(uri, callback);
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException {
return MockContentProvider.this.uncanonicalize(uri);
@@ -292,6 +298,18 @@
/**
* @hide
*/
+ @SuppressWarnings("deprecation")
+ public void canonicalizeAsync(Uri uri, RemoteCallback callback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, canonicalize(uri));
+ callback.sendResult(bundle);
+ });
+ }
+
+ /**
+ * @hide
+ */
public boolean refresh(Uri url, Bundle args) {
throw new UnsupportedOperationException("unimplemented mock method call");
}
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 359c448..2c66047 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -834,6 +834,12 @@
/** @hide */
@Override
+ public Display getDisplayNoVerify() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
public int getDisplayId() {
throw new UnsupportedOperationException();
}
diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java
index 1831bcd..223bcc5 100644
--- a/test-mock/src/android/test/mock/MockIContentProvider.java
+++ b/test-mock/src/android/test/mock/MockIContentProvider.java
@@ -145,12 +145,23 @@
}
@Override
- public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri)
- throws RemoteException {
+ public Uri canonicalize(String callingPkg, @Nullable String featureId, Uri uri) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@Override
+ @SuppressWarnings("deprecation")
+ public void canonicalizeAsync(String callingPkg, String featureId, Uri uri,
+ RemoteCallback remoteCallback) {
+ AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+ canonicalize(callingPkg, featureId, uri));
+ remoteCallback.sendResult(bundle);
+ });
+ }
+
+ @Override
public Uri uncanonicalize(String callingPkg, @Nullable String featureId, Uri uri)
throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/DISABLED_TEST_MAPPING
similarity index 100%
rename from tests/BootImageProfileTest/TEST_MAPPING
rename to tests/BootImageProfileTest/DISABLED_TEST_MAPPING
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
index 52f6eba..e616ac4 100644
--- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -27,6 +27,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.TimeUnit;
+
/**
* Runs rollback tests for multiple users.
*/
@@ -41,7 +43,6 @@
@After
public void tearDown() throws Exception {
- getDevice().switchUser(mOriginalUserId);
getDevice().executeShellCommand("pm uninstall com.android.cts.install.lib.testapp.A");
removeSecondaryUserIfNecessary();
}
@@ -49,9 +50,9 @@
@Before
public void setup() throws Exception {
mOriginalUserId = getDevice().getCurrentUser();
- installPackageAsUser("RollbackTest.apk", true, mOriginalUserId);
- createAndSwitchToSecondaryUserIfNecessary();
- installPackageAsUser("RollbackTest.apk", true, mSecondaryUserId);
+ createAndStartSecondaryUser();
+ // TODO(b/149733368): Remove the '-g' workaround when the bug is fixed.
+ installPackage("RollbackTest.apk", "-g --user all");
}
@Test
@@ -64,7 +65,6 @@
runPhaseForUsers("testMultipleUsersInstallV1", mOriginalUserId, mSecondaryUserId);
runPhaseForUsers("testMultipleUsersUpgradeToV2", mOriginalUserId);
runPhaseForUsers("testMultipleUsersUpdateUserData", mOriginalUserId, mSecondaryUserId);
- switchToUser(mOriginalUserId);
getDevice().executeShellCommand("pm rollback-app com.android.cts.install.lib.testapp.A");
runPhaseForUsers("testMultipleUsersVerifyUserdataRollback", mOriginalUserId,
mSecondaryUserId);
@@ -74,11 +74,11 @@
* Run the phase for the given user ids, in the order they are given.
*/
private void runPhaseForUsers(String phase, int... userIds) throws Exception {
+ final long timeout = TimeUnit.MINUTES.toMillis(10);
for (int userId: userIds) {
- switchToUser(userId);
- assertTrue(runDeviceTests("com.android.tests.rollback",
+ assertTrue(runDeviceTests(getDevice(), "com.android.tests.rollback",
"com.android.tests.rollback.MultiUserRollbackTest",
- phase));
+ phase, userId, timeout));
}
}
@@ -89,21 +89,7 @@
}
}
- private void createAndSwitchToSecondaryUserIfNecessary() throws Exception {
- if (mSecondaryUserId == -1) {
- mOriginalUserId = getDevice().getCurrentUser();
- mSecondaryUserId = getDevice().createUser("MultiUserRollbackTest_User"
- + System.currentTimeMillis());
- switchToUser(mSecondaryUserId);
- }
- }
-
- private void switchToUser(int userId) throws Exception {
- if (getDevice().getCurrentUser() == userId) {
- return;
- }
-
- assertTrue(getDevice().switchUser(userId));
+ private void awaitUserUnlocked(int userId) throws Exception {
for (int i = 0; i < SWITCH_USER_COMPLETED_NUMBER_OF_POLLS; ++i) {
String userState = getDevice().executeShellCommand("am get-started-user-state "
+ userId);
@@ -112,6 +98,14 @@
}
Thread.sleep(SWITCH_USER_COMPLETED_POLL_INTERVAL_IN_MILLIS);
}
- fail("User switch to user " + userId + " timed out");
+ fail("Timed out in unlocking user: " + userId);
+ }
+
+ private void createAndStartSecondaryUser() throws Exception {
+ String name = "MultiUserRollbackTest_User" + System.currentTimeMillis();
+ mSecondaryUserId = getDevice().createUser(name);
+ getDevice().startUser(mSecondaryUserId);
+ // Note we can't install apps on a locked user
+ awaitUserUnlocked(mSecondaryUserId);
}
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
index 0ffe041..400bb04 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/MultiUserRollbackTest.java
@@ -17,13 +17,11 @@
package com.android.tests.rollback;
import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
-import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage;
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
import android.content.rollback.RollbackInfo;
-import android.content.rollback.RollbackManager;
import com.android.cts.install.lib.Install;
import com.android.cts.install.lib.InstallUtils;
@@ -77,13 +75,10 @@
*/
@Test
public void testMultipleUsersUpgradeToV2() throws Exception {
- RollbackManager rm = RollbackUtils.getRollbackManager();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
Install.single(TestApp.A2).setEnableRollback().commit();
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNotNull();
+ RollbackInfo rollback = RollbackUtils.waitForAvailableRollback(TestApp.A);
assertThat(rollback).packagesContainsExactly(
Rollback.from(TestApp.A2).to(TestApp.A1));
}
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 638b6d1..480b12b 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -50,6 +50,7 @@
<application>
<uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.net.ipsec.ike" />
</application>
<instrumentation
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index 490c467..23caf49 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -26,6 +26,7 @@
import android.os.IBinder
import com.android.networkstack.metrics.DataStallStatsUtils
import com.android.networkstack.netlink.TcpSocketTracker
+import com.android.server.NetworkStackService
import com.android.server.NetworkStackService.NetworkMonitorConnector
import com.android.server.NetworkStackService.NetworkStackConnector
import com.android.server.connectivity.NetworkMonitor
@@ -88,6 +89,7 @@
val nm = NetworkMonitor(this@TestNetworkStackService, cb,
this.network,
mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
+ mock(NetworkStackService.NetworkStackServiceManager::class.java),
NetworkMonitorDeps(privateDnsBypassNetwork),
mock(DataStallStatsUtils::class.java),
mock(TcpSocketTracker::class.java))
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index d6a2176..2273bc6 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
import android.test.mock.MockContext;
@@ -232,10 +231,12 @@
builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
final VpnProfile profile = builder.build().toVpnProfile();
+ final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
+ + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
verifyVpnProfileCommon(profile);
assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
assertEquals(
- Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()),
+ expectedSecret,
profile.ipsecSecret);
assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 220cdce..6d4a1b2 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1180,6 +1180,10 @@
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -3042,7 +3046,7 @@
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
- ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
+ ConnectivityManager.TYPE_WIFI, mContext.getPackageName());
});
class NonParcelableSpecifier extends NetworkSpecifier {
@@ -6439,17 +6443,89 @@
assertEquals(wifiLp, mService.getActiveLinkProperties());
}
+ private void setupLocationPermissions(
+ int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdk;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+ if (op != null) {
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ if (perm != null) {
+ mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+ }
+ }
+
+ private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+ final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
+
+ return mService
+ .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName())
+ .getOwnerUid();
+ }
+
@Test
- public void testNetworkCapabilitiesRestrictedForCallerPermissions() {
- int callerUid = Process.myUid();
- final NetworkCapabilities originalNc = new NetworkCapabilities();
- originalNc.setOwnerUid(callerUid);
+ public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
- final NetworkCapabilities newNc =
- mService.networkCapabilitiesRestrictedForCallerPermissions(
- originalNc, Process.myPid(), callerUid);
+ final int myUid = Process.myUid();
+ assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
- assertEquals(Process.INVALID_UID, newNc.getOwnerUid());
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception {
+ // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
+ setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception {
+ // Test that even with fine location permission, not being the owner leads to sanitization.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception {
+ // Test that not having fine location permission leads to sanitization.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ // Test that without the location permission, the owner field is sanitized.
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
+
+ // Test that without the location permission, the owner field is sanitized.
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
}
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -6735,21 +6811,6 @@
mContext.getOpPackageName()));
}
- private void setupLocationPermissions(
- int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.targetSdkVersion = targetSdk;
- when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
- .thenReturn(applicationInfo);
-
- when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
-
- when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
- .thenReturn(AppOpsManager.MODE_ALLOWED);
-
- mServiceContext.setPermission(perm, PERMISSION_GRANTED);
- }
-
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 0e3b797..1994d1f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -59,9 +59,15 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
+import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService;
@@ -84,6 +90,7 @@
import com.android.internal.R;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.server.IpSecService;
import org.junit.Before;
import org.junit.Test;
@@ -93,6 +100,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -125,6 +133,9 @@
}
static final String TEST_VPN_PKG = "com.dummy.vpn";
+ private static final String TEST_VPN_SERVER = "1.2.3.4";
+ private static final String TEST_VPN_IDENTITY = "identity";
+ private static final byte[] TEST_VPN_PSK = "psk".getBytes();
/**
* Names and UIDs for some fake packages. Important points:
@@ -151,23 +162,39 @@
@Mock private Vpn.SystemServices mSystemServices;
@Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private IpSecService mIpSecService;
@Mock private KeyStore mKeyStore;
- private final VpnProfile mVpnProfile = new VpnProfile("key");
+ private final VpnProfile mVpnProfile;
+
+ private IpSecManager mIpSecManager;
+
+ public VpnTest() throws Exception {
+ // Build an actual VPN profile that is capable of being converted to and from an
+ // Ikev2VpnProfile
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY);
+ builder.setAuthPsk(TEST_VPN_PSK);
+ mVpnProfile = builder.build().toVpnProfile();
+ }
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mIpSecManager = new IpSecManager(mContext, mIpSecService);
+
when(mContext.getPackageManager()).thenReturn(mPackageManager);
setMockedPackages(mPackages);
- when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
+ when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
.thenReturn(mNotificationManager);
when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
.thenReturn(mConnectivityManager);
+ when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -962,6 +989,26 @@
// a subsequent CL.
}
+ @Test
+ public void testStartLegacyVpn() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ // Dummy egress interface
+ final String egressIface = "DUMMY0";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(egressIface);
+
+ final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ InetAddresses.parseNumericAddress("192.0.2.0"), egressIface);
+ lp.addRoute(defaultRoute);
+
+ vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp);
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
+ }
+
/**
* Mock some methods of vpn object.
*/
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 957216e..26916bc 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -38,6 +38,7 @@
"android.app.activity.ActivityThreadClientTest",
// Test specifications for FrameworksCoreTests.
"android.app.servertransaction.", // all tests under the package.
+ "android.view.CutoutSpecificationTest",
"android.view.DisplayCutoutTest",
"android.view.InsetsAnimationControlImplTest",
"android.view.InsetsControllerTest",
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
index 9661927..6a5b0e1 100755
--- a/tools/hiddenapi/merge_csv.py
+++ b/tools/hiddenapi/merge_csv.py
@@ -14,26 +14,56 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
-Merge mutliple CSV files, possibly with different columns, writing to stdout.
+Merge multiple CSV files, possibly with different columns.
"""
+import argparse
import csv
-import sys
+import io
-csv_readers = [
- csv.DictReader(open(csv_file, 'r'), delimiter=',', quotechar='|')
- for csv_file in sys.argv[1:]
-]
+from zipfile import ZipFile
-# Build union of all columns from source files:
+args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
+args_parser.add_argument('--header', help='Comma separated field names; '
+ 'if missing determines the header from input files.')
+args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
+args_parser.add_argument('--output', help='Output file for merged CSV.',
+ default='-', type=argparse.FileType('w'))
+args_parser.add_argument('files', nargs=argparse.REMAINDER)
+args = args_parser.parse_args()
+
+
+def dict_reader(input):
+ return csv.DictReader(input, delimiter=',', quotechar='|')
+
+
+if args.zip_input and len(args.files) > 0:
+ raise ValueError('Expecting either a single ZIP with CSV files'
+ ' or a list of CSV files as input; not both.')
+
+csv_readers = []
+if len(args.files) > 0:
+ for file in args.files:
+ csv_readers.append(dict_reader(open(file, 'r')))
+elif args.zip_input:
+ with ZipFile(args.zip_input) as zip:
+ for entry in zip.namelist():
+ if entry.endswith('.uau'):
+ csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+
headers = set()
-for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
+if args.header:
+ fieldnames = args.header.split(',')
+else:
+ # Build union of all columns from source files:
+ for reader in csv_readers:
+ headers = headers.union(reader.fieldnames)
+ fieldnames = sorted(headers)
# Concatenate all files to output:
-out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
- dialect='unix', fieldnames=sorted(headers))
-out.writeheader()
+writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+ dialect='unix', fieldnames=fieldnames)
+writer.writeheader()
for reader in csv_readers:
for row in reader:
- out.writerow(row)
+ writer.writerow(row)
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e1450cb..1a12af3 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2167,6 +2167,7 @@
sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
+ .append(" HOME-PROVIDER-NETWORK: ").append(this.isHomeProviderNetwork)
.append(" PRIO: ").append(this.priority)
.append(" HIDDEN: ").append(this.hiddenSSID)
.append(" PMF: ").append(this.requirePmf)
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index af2f246..bab9a9e 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -46,6 +46,7 @@
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.ProvisioningCallback;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -4920,7 +4921,10 @@
}
/** @hide */
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(
+ maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "Use {@code #setVerboseLoggingEnabled(boolean)} instead."
+ )
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
public void enableVerboseLogging (int verbose) {
try {
@@ -4948,7 +4952,10 @@
/** @hide */
// TODO(b/145484145): remove once SUW stops calling this via reflection
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(
+ maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "Use {@code #isVerboseLoggingEnabled()} instead."
+ )
public int getVerboseLoggingLevel() {
try {
return mService.getVerboseLoggingLevel();
diff --git a/wifi/java/android/net/wifi/WifiOemMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
index 44dbb98..5301dd0 100755
--- a/wifi/java/android/net/wifi/WifiOemMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -437,7 +437,7 @@
Settings.Global.WIFI_SCAN_THROTTLE_ENABLED, 1) == 1)
.setVerboseLoggingEnabled(
Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 1) == 1)
+ Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0) == 1)
.build();
}
}