diff options
320 files changed, 6665 insertions, 2407 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 284e807bfc45..23dc7207343c 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -123,13 +123,19 @@ droidstubs { }, dists: [ { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/public/api", dest: "android-non-updatable.txt", tag: ".api.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/public/api", dest: "android-non-updatable-removed.txt", tag: ".removed-api.txt", @@ -137,21 +143,18 @@ droidstubs { ], } -priv_apps = - " --show-annotation android.annotation.SystemApi\\(" + - "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" + +priv_apps = " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" + "\\)" -priv_apps_in_stubs = - " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" + - "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" + +priv_apps_in_stubs = " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" + "\\)" test = " --show-annotation android.annotation.TestApi" -module_libs = - " --show-annotation android.annotation.SystemApi\\(" + - "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" + +module_libs = " --show-annotation android.annotation.SystemApi\\(" + + "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" + "\\)" droidstubs { @@ -166,7 +169,7 @@ droidstubs { last_released: { api_file: ":android-non-updatable.api.system.latest", removed_api_file: ":android-non-updatable-removed.api.system.latest", - baseline_file: ":android-non-updatable-incompatibilities.api.system.latest" + baseline_file: ":android-non-updatable-incompatibilities.api.system.latest", }, api_lint: { enabled: true, @@ -176,13 +179,19 @@ droidstubs { }, dists: [ { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/system/api", dest: "android-non-updatable.txt", tag: ".api.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/system/api", dest: "android-non-updatable-removed.txt", tag: ".removed-api.txt", @@ -206,25 +215,37 @@ droidstubs { }, dists: [ { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/test/api", dest: "android.txt", tag: ".api.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/test/api", dest: "removed.txt", tag: ".removed-api.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/test/api", dest: "android-non-updatable.txt", tag: ".api.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/test/api", dest: "android-non-updatable-removed.txt", tag: ".removed-api.txt", @@ -252,13 +273,19 @@ droidstubs { }, dists: [ { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/module-lib/api", dest: "android-non-updatable.txt", tag: ".api.txt", }, { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], dir: "apistubs/android/module-lib/api", dest: "android-non-updatable-removed.txt", tag: ".removed-api.txt", @@ -318,10 +345,13 @@ java_defaults { java_version: "1.8", compile_dex: true, dist: { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], tag: ".jar", dest: "android-non-updatable.jar", - } + }, } java_library_static { @@ -337,7 +367,7 @@ java_library_static { java_library_static { name: "android-non-updatable.stubs.system", defaults: ["android-non-updatable_defaults_stubs_current"], - srcs: [ ":system-api-stubs-docs-non-updatable" ], + srcs: [":system-api-stubs-docs-non-updatable"], libs: modules_system_stubs, dist: { dir: "apistubs/android/system", @@ -380,7 +410,10 @@ java_defaults { java_defaults { name: "android_stubs_dists_default", dist: { - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], tag: ".jar", dest: "android.jar", }, @@ -411,7 +444,10 @@ java_library_static { dists: [ { // Legacy dist path - targets: ["sdk", "win_sdk"], + targets: [ + "sdk", + "win_sdk", + ], tag: ".jar", dest: "android_system.jar", }, @@ -433,14 +469,6 @@ java_library_static { dist: { dir: "apistubs/android/test", }, - dists: [ - { - // Legacy dist path - targets: ["sdk", "win_sdk"], - tag: ".jar", - dest: "android_test.jar", - }, - ], } java_library_static { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index c3f197853872..11eb5ca7909b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -16,6 +16,7 @@ package com.android.server.appsearch; import static android.app.appsearch.AppSearchResult.throwableToFailedResult; +import static android.os.Process.INVALID_UID; import static android.os.UserHandle.USER_NULL; import android.annotation.ElapsedRealtimeLong; @@ -36,6 +37,7 @@ import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.StorageInfo; +import android.app.appsearch.exceptions.AppSearchException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -52,7 +54,6 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; @@ -122,6 +123,14 @@ public class AppSearchManagerService extends SystemService { mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL, new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null, /*scheduler=*/ null); + + IntentFilter packageChangedFilter = new IntentFilter(); + packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); + packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); + packageChangedFilter.addDataScheme("package"); + mContext.registerReceiverAsUser(new PackageChangedReceiver(), UserHandle.ALL, + packageChangedFilter, /*broadcastPermission=*/ null, + /*scheduler=*/ null); } private class UserActionReceiver extends BroadcastReceiver { @@ -129,15 +138,15 @@ public class AppSearchManagerService extends SystemService { public void onReceive(@NonNull Context context, @NonNull Intent intent) { switch (intent.getAction()) { case Intent.ACTION_USER_REMOVED: - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); if (userId == USER_NULL) { - Slog.e(TAG, "userId is missing in the intent: " + intent); + Log.e(TAG, "userId is missing in the intent: " + intent); return; } handleUserRemoved(userId); break; default: - Slog.e(TAG, "Received unknown intent: " + intent); + Log.e(TAG, "Received unknown intent: " + intent); } } } @@ -157,9 +166,44 @@ public class AppSearchManagerService extends SystemService { try { mImplInstanceManager.removeAppSearchImplForUser(userId); mLoggerInstanceManager.removePlatformLoggerForUser(userId); - Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId); + Log.i(TAG, "Removed AppSearchImpl instance for user: " + userId); } catch (Throwable t) { - Slog.e(TAG, "Unable to remove data for user: " + userId, t); + Log.e(TAG, "Unable to remove data for user: " + userId, t); + } + } + + private class PackageChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + switch (intent.getAction()) { + case Intent.ACTION_PACKAGE_FULLY_REMOVED: + case Intent.ACTION_PACKAGE_DATA_CLEARED: + String packageName = intent.getData().getSchemeSpecificPart(); + if (packageName == null) { + Log.e(TAG, "Package name is missing in the intent: " + intent); + return; + } + int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); + if (uid == INVALID_UID) { + Log.e(TAG, "uid is missing in the intent: " + intent); + return; + } + handlePackageRemoved(packageName, uid); + break; + default: + Log.e(TAG, "Received unknown intent: " + intent); + } + } + } + + private void handlePackageRemoved(String packageName, int uid) { + int userId = UserHandle.getUserId(uid); + try { + AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userId); + //TODO(b/145759910) clear visibility setting for package. + impl.clearPackageData(packageName); + } catch (AppSearchException e) { + Log.e(TAG, "Unable to remove data for package: " + packageName, e); } } diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java index 113f8fe9e248..6dbbcb564b4c 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java @@ -26,8 +26,8 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.util.Base64; +import android.util.IndentingPrintWriter; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java index ca588c509594..09260b775444 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java @@ -37,9 +37,9 @@ import android.permission.PermissionManager; import android.util.ArraySet; import android.util.Base64; import android.util.DebugUtils; +import android.util.IndentingPrintWriter; import android.util.Slog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java index 8b12beb57195..e47715685323 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -15,6 +15,7 @@ */ package com.android.server.blob; +import static android.Manifest.permission.ACCESS_BLOBS_ACROSS_USERS; import static android.app.blob.XmlTags.ATTR_COMMIT_TIME_MS; import static android.app.blob.XmlTags.ATTR_DESCRIPTION; import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME; @@ -36,6 +37,7 @@ import static com.android.server.blob.BlobStoreConfig.TAG; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_COMMIT_TIME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC; +import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed; import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId; import static com.android.server.blob.BlobStoreUtils.getPackageResources; @@ -45,15 +47,18 @@ import android.annotation.Nullable; import android.app.blob.BlobHandle; import android.app.blob.LeaseInfo; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.ResourceId; import android.content.res.Resources; import android.os.ParcelFileDescriptor; import android.os.RevocableFileDescriptor; import android.os.UserHandle; +import android.permission.PermissionManager; import android.system.ErrnoException; import android.system.Os; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; @@ -62,7 +67,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; @@ -85,7 +89,6 @@ class BlobMetadata { private final long mBlobId; private final BlobHandle mBlobHandle; - private final int mUserId; @GuardedBy("mMetadataLock") private final ArraySet<Committer> mCommitters = new ArraySet<>(); @@ -94,24 +97,23 @@ class BlobMetadata { private final ArraySet<Leasee> mLeasees = new ArraySet<>(); /** - * Contains packageName -> {RevocableFileDescriptors}. + * Contains Accessor -> {RevocableFileDescriptors}. * * Keep track of RevocableFileDescriptors given to clients which are not yet revoked/closed so * that when clients access is revoked or the blob gets deleted, we can be sure that clients * do not have any reference to the blob and the space occupied by the blob can be freed. */ @GuardedBy("mRevocableFds") - private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds = + private final ArrayMap<Accessor, ArraySet<RevocableFileDescriptor>> mRevocableFds = new ArrayMap<>(); // Do not access this directly, instead use #getBlobFile(). private File mBlobFile; - BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) { + BlobMetadata(Context context, long blobId, BlobHandle blobHandle) { mContext = context; this.mBlobId = blobId; this.mBlobHandle = blobHandle; - this.mUserId = userId; } long getBlobId() { @@ -122,10 +124,6 @@ class BlobMetadata { return mBlobHandle; } - int getUserId() { - return mUserId; - } - void addOrReplaceCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing @@ -155,13 +153,24 @@ class BlobMetadata { } } - void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) { + void removeCommittersFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) { synchronized (mMetadataLock) { - mCommitters.removeIf(committer -> - !committer.packageName.equals(knownPackages.get(committer.uid))); + mCommitters.removeIf(committer -> { + final int userId = UserHandle.getUserId(committer.uid); + final SparseArray<String> userPackages = knownPackages.get(userId); + if (userPackages == null) { + return true; + } + return !committer.packageName.equals(userPackages.get(committer.uid)); + }); } } + void addCommittersAndLeasees(BlobMetadata blobMetadata) { + mCommitters.addAll(blobMetadata.mCommitters); + mLeasees.addAll(blobMetadata.mLeasees); + } + @Nullable Committer getExistingCommitter(@NonNull String packageName, int uid) { synchronized (mCommitters) { @@ -201,10 +210,16 @@ class BlobMetadata { } } - void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) { + void removeLeaseesFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) { synchronized (mMetadataLock) { - mLeasees.removeIf(leasee -> - !leasee.packageName.equals(knownPackages.get(leasee.uid))); + mLeasees.removeIf(leasee -> { + final int userId = UserHandle.getUserId(leasee.uid); + final SparseArray<String> userPackages = knownPackages.get(userId); + if (userPackages == null) { + return true; + } + return !leasee.packageName.equals(userPackages.get(leasee.uid)); + }); } } @@ -214,6 +229,25 @@ class BlobMetadata { } } + void removeDataForUser(int userId) { + synchronized (mMetadataLock) { + mCommitters.removeIf(committer -> (userId == UserHandle.getUserId(committer.uid))); + mLeasees.removeIf(leasee -> (userId == UserHandle.getUserId(leasee.uid))); + mRevocableFds.entrySet().removeIf(entry -> { + final Accessor accessor = entry.getKey(); + final ArraySet<RevocableFileDescriptor> rFds = entry.getValue(); + if (userId != UserHandle.getUserId(accessor.uid)) { + return false; + } + for (int i = 0, fdCount = rFds.size(); i < fdCount; ++i) { + rFds.valueAt(i).revoke(); + } + rFds.clear(); + return true; + }); + } + } + boolean hasValidLeases() { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { @@ -244,8 +278,12 @@ class BlobMetadata { } } + final int callingUserId = UserHandle.getUserId(callingUid); for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); + if (callingUserId != UserHandle.getUserId(committer.uid)) { + continue; + } // Check if the caller is the same package that committed the blob. if (committer.equals(callingPackage, callingUid)) { @@ -259,38 +297,105 @@ class BlobMetadata { return true; } } + + final boolean canCallerAccessBlobsAcrossUsers = checkCallerCanAccessBlobsAcrossUsers( + callingPackage, callingUserId); + if (!canCallerAccessBlobsAcrossUsers) { + return false; + } + for (int i = 0, size = mCommitters.size(); i < size; ++i) { + final Committer committer = mCommitters.valueAt(i); + final int committerUserId = UserHandle.getUserId(committer.uid); + if (callingUserId == committerUserId) { + continue; + } + if (!checkCallerCanAccessBlobsAcrossUsers(callingPackage, committerUserId)) { + continue; + } + + // Check if the caller is allowed access as per the access mode specified + // by the committer. + if (committer.blobAccessMode.isAccessAllowedForCaller(mContext, + callingPackage, committer.packageName, callingUid, attributionTag)) { + return true; + } + } + + } + return false; + } + + private static boolean checkCallerCanAccessBlobsAcrossUsers( + String callingPackage, int callingUserId) { + return PermissionManager.checkPackageNamePermission(ACCESS_BLOBS_ACROSS_USERS, + callingPackage, callingUserId) == PackageManager.PERMISSION_GRANTED; + } + + boolean hasACommitterOrLeaseeInUser(int userId) { + return hasACommitterInUser(userId) || hasALeaseeInUser(userId); + } + + boolean hasACommitterInUser(int userId) { + synchronized (mMetadataLock) { + for (int i = 0, size = mCommitters.size(); i < size; ++i) { + final Committer committer = mCommitters.valueAt(i); + if (userId == UserHandle.getUserId(committer.uid)) { + return true; + } + } + } + return false; + } + + private boolean hasALeaseeInUser(int userId) { + synchronized (mMetadataLock) { + for (int i = 0, size = mLeasees.size(); i < size; ++i) { + final Leasee leasee = mLeasees.valueAt(i); + if (userId == UserHandle.getUserId(leasee.uid)) { + return true; + } + } } return false; } boolean isACommitter(@NonNull String packageName, int uid) { synchronized (mMetadataLock) { - return isAnAccessor(mCommitters, packageName, uid); + return isAnAccessor(mCommitters, packageName, uid, UserHandle.getUserId(uid)); } } boolean isALeasee(@Nullable String packageName, int uid) { synchronized (mMetadataLock) { - final Leasee leasee = getAccessor(mLeasees, packageName, uid); + final Leasee leasee = getAccessor(mLeasees, packageName, uid, + UserHandle.getUserId(uid)); + return leasee != null && leasee.isStillValid(); + } + } + + private boolean isALeaseeInUser(@Nullable String packageName, int uid, int userId) { + synchronized (mMetadataLock) { + final Leasee leasee = getAccessor(mLeasees, packageName, uid, userId); return leasee != null && leasee.isStillValid(); } } private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors, - @Nullable String packageName, int uid) { + @Nullable String packageName, int uid, int userId) { // Check if the package is an accessor of the data blob. - return getAccessor(accessors, packageName, uid) != null; + return getAccessor(accessors, packageName, uid, userId) != null; } private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors, - @Nullable String packageName, int uid) { + @Nullable String packageName, int uid, int userId) { // Check if the package is an accessor of the data blob. for (int i = 0, size = accessors.size(); i < size; ++i) { final Accessor accessor = accessors.valueAt(i); if (packageName != null && uid != INVALID_UID && accessor.equals(packageName, uid)) { return (T) accessor; - } else if (packageName != null && accessor.packageName.equals(packageName)) { + } else if (packageName != null && accessor.packageName.equals(packageName) + && userId == UserHandle.getUserId(accessor.uid)) { return (T) accessor; } else if (uid != INVALID_UID && accessor.uid == uid) { return (T) accessor; @@ -299,23 +404,29 @@ class BlobMetadata { return null; } - boolean isALeasee(@NonNull String packageName) { - return isALeasee(packageName, INVALID_UID); - } - - boolean isALeasee(int uid) { - return isALeasee(null, uid); - } - - boolean hasOtherLeasees(@NonNull String packageName) { - return hasOtherLeasees(packageName, INVALID_UID); + boolean shouldAttributeToLeasee(@NonNull String packageName, int userId, + boolean callerHasStatsPermission) { + if (!isALeaseeInUser(packageName, INVALID_UID, userId)) { + return false; + } + if (!callerHasStatsPermission || !hasOtherLeasees(packageName, INVALID_UID, userId)) { + return true; + } + return false; } - boolean hasOtherLeasees(int uid) { - return hasOtherLeasees(null, uid); + boolean shouldAttributeToLeasee(int uid, boolean callerHasStatsPermission) { + final int userId = UserHandle.getUserId(uid); + if (!isALeaseeInUser(null, uid, userId)) { + return false; + } + if (!callerHasStatsPermission || !hasOtherLeasees(null, uid, userId)) { + return true; + } + return false; } - private boolean hasOtherLeasees(@Nullable String packageName, int uid) { + private boolean hasOtherLeasees(@Nullable String packageName, int uid, int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); @@ -326,7 +437,8 @@ class BlobMetadata { if (packageName != null && uid != INVALID_UID && !leasee.equals(packageName, uid)) { return true; - } else if (packageName != null && !leasee.packageName.equals(packageName)) { + } else if (packageName != null && (!leasee.packageName.equals(packageName) + || userId != UserHandle.getUserId(leasee.uid))) { return true; } else if (uid != INVALID_UID && leasee.uid != uid) { return true; @@ -371,7 +483,7 @@ class BlobMetadata { return mBlobFile; } - ParcelFileDescriptor openForRead(String callingPackage) throws IOException { + ParcelFileDescriptor openForRead(String callingPackage, int callingUid) throws IOException { // TODO: Add limit on opened fds FileDescriptor fd; try { @@ -381,7 +493,7 @@ class BlobMetadata { } try { if (BlobStoreConfig.shouldUseRevocableFdForReads()) { - return createRevocableFd(fd, callingPackage); + return createRevocableFd(fd, callingPackage, callingUid); } else { return new ParcelFileDescriptor(fd); } @@ -393,26 +505,28 @@ class BlobMetadata { @NonNull private ParcelFileDescriptor createRevocableFd(FileDescriptor fd, - String callingPackage) throws IOException { + String callingPackage, int callingUid) throws IOException { final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd); + final Accessor accessor; synchronized (mRevocableFds) { - ArraySet<RevocableFileDescriptor> revocableFdsForPkg = - mRevocableFds.get(callingPackage); - if (revocableFdsForPkg == null) { - revocableFdsForPkg = new ArraySet<>(); - mRevocableFds.put(callingPackage, revocableFdsForPkg); + accessor = new Accessor(callingPackage, callingUid); + ArraySet<RevocableFileDescriptor> revocableFdsForAccessor = + mRevocableFds.get(accessor); + if (revocableFdsForAccessor == null) { + revocableFdsForAccessor = new ArraySet<>(); + mRevocableFds.put(accessor, revocableFdsForAccessor); } - revocableFdsForPkg.add(revocableFd); + revocableFdsForAccessor.add(revocableFd); } revocableFd.addOnCloseListener((e) -> { synchronized (mRevocableFds) { - final ArraySet<RevocableFileDescriptor> revocableFdsForPkg = - mRevocableFds.get(callingPackage); - if (revocableFdsForPkg != null) { - revocableFdsForPkg.remove(revocableFd); - if (revocableFdsForPkg.isEmpty()) { - mRevocableFds.remove(callingPackage); + final ArraySet<RevocableFileDescriptor> revocableFdsForAccessor = + mRevocableFds.get(accessor); + if (revocableFdsForAccessor != null) { + revocableFdsForAccessor.remove(revocableFd); + if (revocableFdsForAccessor.isEmpty()) { + mRevocableFds.remove(accessor); } } } @@ -421,22 +535,23 @@ class BlobMetadata { } void destroy() { - revokeAllFds(); + revokeAndClearAllFds(); getBlobFile().delete(); } - private void revokeAllFds() { + private void revokeAndClearAllFds() { synchronized (mRevocableFds) { - for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) { - final ArraySet<RevocableFileDescriptor> packageFds = + for (int i = 0, accessorCount = mRevocableFds.size(); i < accessorCount; ++i) { + final ArraySet<RevocableFileDescriptor> rFds = mRevocableFds.valueAt(i); - if (packageFds == null) { + if (rFds == null) { continue; } - for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) { - packageFds.valueAt(j).revoke(); + for (int j = 0, fdCount = rFds.size(); j < fdCount; ++j) { + rFds.valueAt(j).revoke(); } } + mRevocableFds.clear(); } } @@ -547,10 +662,10 @@ class BlobMetadata { fout.println("<empty>"); } else { for (int i = 0, count = mRevocableFds.size(); i < count; ++i) { - final String packageName = mRevocableFds.keyAt(i); - final ArraySet<RevocableFileDescriptor> packageFds = + final Accessor accessor = mRevocableFds.keyAt(i); + final ArraySet<RevocableFileDescriptor> rFds = mRevocableFds.valueAt(i); - fout.println(packageName + "#" + packageFds.size()); + fout.println(accessor + ": #" + rFds.size()); } } fout.decreaseIndent(); @@ -560,7 +675,6 @@ class BlobMetadata { void writeToXml(XmlSerializer out) throws IOException { synchronized (mMetadataLock) { XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId); - XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId); out.startTag(null, TAG_BLOB_HANDLE); mBlobHandle.writeToXml(out); @@ -584,7 +698,9 @@ class BlobMetadata { static BlobMetadata createFromXml(XmlPullParser in, int version, Context context) throws XmlPullParserException, IOException { final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID); - final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID); + if (version < XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) { + XmlUtils.readIntAttribute(in, ATTR_USER_ID); + } BlobHandle blobHandle = null; final ArraySet<Committer> committers = new ArraySet<>(); @@ -608,7 +724,7 @@ class BlobMetadata { return null; } - final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId); + final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle); blobMetadata.setCommitters(committers); blobMetadata.setLeasees(leasees); return blobMetadata; diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java index 5cebf8d91cfc..502b29eb1a1f 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java @@ -27,12 +27,11 @@ import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.text.TextUtils; import android.util.DataUnit; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; -import com.android.internal.util.IndentingPrintWriter; - import java.io.File; import java.util.concurrent.TimeUnit; @@ -47,8 +46,9 @@ class BlobStoreConfig { public static final int XML_VERSION_ADD_DESC_RES_NAME = 3; public static final int XML_VERSION_ADD_COMMIT_TIME = 4; public static final int XML_VERSION_ADD_SESSION_CREATION_TIME = 5; + public static final int XML_VERSION_ALLOW_ACCESS_ACROSS_USERS = 6; - public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME; + public static final int XML_VERSION_CURRENT = XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; public static final long INVALID_BLOB_ID = 0; public static final long INVALID_BLOB_SIZE = 0; 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 0e7354726123..cc5e31a91123 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -32,6 +32,7 @@ import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_ID; import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE; import static com.android.server.blob.BlobStoreConfig.LOGV; import static com.android.server.blob.BlobStoreConfig.TAG; +import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT; import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs; import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs; @@ -83,6 +84,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.ExceptionUtils; +import android.util.IndentingPrintWriter; import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; @@ -96,7 +98,6 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; @@ -129,6 +130,7 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -146,9 +148,9 @@ public class BlobStoreManagerService extends SystemService { @GuardedBy("mBlobsLock") private long mCurrentMaxSessionId; - // Contains data of userId -> {BlobHandle -> {BlobMetadata}} + // Contains data of BlobHandle -> BlobMetadata. @GuardedBy("mBlobsLock") - private final SparseArray<ArrayMap<BlobHandle, BlobMetadata>> mBlobsMap = new SparseArray<>(); + private final ArrayMap<BlobHandle, BlobMetadata> mBlobsMap = new ArrayMap<>(); // Contains all ids that are currently in use. @GuardedBy("mBlobsLock") @@ -265,27 +267,24 @@ public class BlobStoreManagerService extends SystemService { return userSessions; } - @GuardedBy("mBlobsLock") - private ArrayMap<BlobHandle, BlobMetadata> getUserBlobsLocked(int userId) { - ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.get(userId); - if (userBlobs == null) { - userBlobs = new ArrayMap<>(); - mBlobsMap.put(userId, userBlobs); + @VisibleForTesting + void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) { + synchronized (mBlobsLock) { + mSessions.put(userId, userSessions); } - return userBlobs; } @VisibleForTesting - void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) { + BlobMetadata getBlobForTest(BlobHandle blobHandle) { synchronized (mBlobsLock) { - mSessions.put(userId, userSessions); + return mBlobsMap.get(blobHandle); } } @VisibleForTesting - void addUserBlobsForTest(ArrayMap<BlobHandle, BlobMetadata> userBlobs, int userId) { + int getBlobsCountForTest() { synchronized (mBlobsLock) { - mBlobsMap.put(userId, userBlobs); + return mBlobsMap.size(); } } @@ -319,14 +318,9 @@ public class BlobStoreManagerService extends SystemService { } @GuardedBy("mBlobsLock") - private void addBlobForUserLocked(BlobMetadata blobMetadata, int userId) { - addBlobForUserLocked(blobMetadata, getUserBlobsLocked(userId)); - } - - @GuardedBy("mBlobsLock") - private void addBlobForUserLocked(BlobMetadata blobMetadata, - ArrayMap<BlobHandle, BlobMetadata> userBlobs) { - userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata); + @VisibleForTesting + void addBlobLocked(BlobMetadata blobMetadata) { + mBlobsMap.put(blobMetadata.getBlobHandle(), blobMetadata); addActiveBlobIdLocked(blobMetadata.getBlobId()); } @@ -404,8 +398,7 @@ public class BlobStoreManagerService extends SystemService { private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid, String callingPackage, String attributionTag) throws IOException { synchronized (mBlobsLock) { - final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid)) - .get(blobHandle); + final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle); if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller( callingPackage, callingUid, attributionTag)) { if (blobMetadata == null) { @@ -415,7 +408,7 @@ public class BlobStoreManagerService extends SystemService { } else { FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid, blobMetadata.getBlobId(), blobMetadata.getSize(), - FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED); + FrameworkStatsLog.BLOB_OPENED__RESULT__ACCESS_NOT_ALLOWED); } throw new SecurityException("Caller not allowed to access " + blobHandle + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); @@ -425,7 +418,7 @@ public class BlobStoreManagerService extends SystemService { blobMetadata.getBlobId(), blobMetadata.getSize(), FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS); - return blobMetadata.openForRead(callingPackage); + return blobMetadata.openForRead(callingPackage, callingUid); } } @@ -433,11 +426,11 @@ public class BlobStoreManagerService extends SystemService { private int getCommittedBlobsCountLocked(int uid, String packageName) { // TODO: Maintain a counter instead of traversing all the blobs final AtomicInteger blobsCount = new AtomicInteger(0); - forEachBlobInUser((blobMetadata) -> { + forEachBlobLocked(blobMetadata -> { if (blobMetadata.isACommitter(packageName, uid)) { blobsCount.getAndIncrement(); } - }, UserHandle.getUserId(uid)); + }); return blobsCount.get(); } @@ -445,11 +438,11 @@ public class BlobStoreManagerService extends SystemService { private int getLeasedBlobsCountLocked(int uid, String packageName) { // TODO: Maintain a counter instead of traversing all the blobs final AtomicInteger blobsCount = new AtomicInteger(0); - forEachBlobInUser((blobMetadata) -> { + forEachBlobLocked(blobMetadata -> { if (blobMetadata.isALeasee(packageName, uid)) { blobsCount.getAndIncrement(); } - }, UserHandle.getUserId(uid)); + }); return blobsCount.get(); } @@ -465,8 +458,16 @@ public class BlobStoreManagerService extends SystemService { throw new LimitExceededException("Too many leased blobs for the caller: " + leasesCount); } - final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid)) - .get(blobHandle); + if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0 + && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) { + FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid, + INVALID_BLOB_ID, INVALID_BLOB_SIZE, + FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID); + throw new IllegalArgumentException( + "Lease expiry cannot be later than blobs expiry time"); + } + + final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle); if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller( callingPackage, callingUid, attributionTag)) { if (blobMetadata == null) { @@ -481,15 +482,7 @@ public class BlobStoreManagerService extends SystemService { throw new SecurityException("Caller not allowed to access " + blobHandle + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); } - if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0 - && leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) { - FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid, - blobMetadata.getBlobId(), blobMetadata.getSize(), - FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID); - throw new IllegalArgumentException( - "Lease expiry cannot be later than blobs expiry time"); - } if (blobMetadata.getSize() > getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) { @@ -518,20 +511,18 @@ public class BlobStoreManagerService extends SystemService { @GuardedBy("mBlobsLock") long getTotalUsageBytesLocked(int callingUid, String callingPackage) { final AtomicLong totalBytes = new AtomicLong(0); - forEachBlobInUser((blobMetadata) -> { + forEachBlobLocked((blobMetadata) -> { if (blobMetadata.isALeasee(callingPackage, callingUid)) { totalBytes.getAndAdd(blobMetadata.getSize()); } - }, UserHandle.getUserId(callingUid)); + }); return totalBytes.get(); } private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid, String callingPackage, String attributionTag) { synchronized (mBlobsLock) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = - getUserBlobsLocked(UserHandle.getUserId(callingUid)); - final BlobMetadata blobMetadata = userBlobs.get(blobHandle); + final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle); if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller( callingPackage, callingUid, attributionTag)) { throw new SecurityException("Caller not allowed to access " + blobHandle @@ -547,12 +538,12 @@ public class BlobStoreManagerService extends SystemService { synchronized (mBlobsLock) { // Check if blobMetadata object is still valid. If it is not, then // it means that it was already deleted and nothing else to do here. - if (!Objects.equals(userBlobs.get(blobHandle), blobMetadata)) { + if (!Objects.equals(mBlobsMap.get(blobHandle), blobMetadata)) { return; } if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) { deleteBlobLocked(blobMetadata); - userBlobs.remove(blobHandle); + mBlobsMap.remove(blobHandle); } writeBlobsInfoAsync(); } @@ -583,12 +574,18 @@ public class BlobStoreManagerService extends SystemService { } return packageResources; }; - getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> { + forEachBlobLocked((blobHandle, blobMetadata) -> { + if (!blobMetadata.hasACommitterOrLeaseeInUser(userId)) { + return; + } final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>(); blobMetadata.forEachLeasee(leasee -> { if (!leasee.isStillValid()) { return; } + if (userId != UserHandle.getUserId(leasee.uid)) { + return; + } final int descriptionResId = leasee.descriptionResEntryName == null ? Resources.ID_NULL : getDescriptionResourceId(resourcesGetter.apply(leasee.packageName), @@ -608,9 +605,7 @@ public class BlobStoreManagerService extends SystemService { private void deleteBlobInternal(long blobId, int callingUid) { synchronized (mBlobsLock) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked( - UserHandle.getUserId(callingUid)); - userBlobs.entrySet().removeIf(entry -> { + mBlobsMap.entrySet().removeIf(entry -> { final BlobMetadata blobMetadata = entry.getValue(); if (blobMetadata.getBlobId() == blobId) { deleteBlobLocked(blobMetadata); @@ -625,19 +620,20 @@ public class BlobStoreManagerService extends SystemService { private List<BlobHandle> getLeasedBlobsInternal(int callingUid, @NonNull String callingPackage) { final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>(); - forEachBlobInUser(blobMetadata -> { - if (blobMetadata.isALeasee(callingPackage, callingUid)) { - leasedBlobs.add(blobMetadata.getBlobHandle()); - } - }, UserHandle.getUserId(callingUid)); + synchronized (mBlobsLock) { + forEachBlobLocked(blobMetadata -> { + if (blobMetadata.isALeasee(callingPackage, callingUid)) { + leasedBlobs.add(blobMetadata.getBlobHandle()); + } + }); + } return leasedBlobs; } private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle, int callingUid, @NonNull String callingPackage, String attributionTag) { synchronized (mBlobsLock) { - final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid)) - .get(blobHandle); + final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle); if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller( callingPackage, callingUid, attributionTag)) { throw new SecurityException("Caller not allowed to access " + blobHandle @@ -699,14 +695,14 @@ public class BlobStoreManagerService extends SystemService { FrameworkStatsLog.BLOB_COMMITTED__RESULT__COUNT_LIMIT_EXCEEDED); break; } - final int userId = UserHandle.getUserId(session.getOwnerUid()); - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked( - userId); - BlobMetadata blob = userBlobs.get(session.getBlobHandle()); - if (blob == null) { + final BlobMetadata blob; + final int blobIndex = mBlobsMap.indexOfKey(session.getBlobHandle()); + if (blobIndex >= 0) { + blob = mBlobsMap.valueAt(blobIndex); + } else { blob = new BlobMetadata(mContext, session.getSessionId(), - session.getBlobHandle(), userId); - addBlobForUserLocked(blob, userBlobs); + session.getBlobHandle()); + addBlobLocked(blob); } final Committer existingCommitter = blob.getExistingCommitter( session.getOwnerPackageName(), session.getOwnerUid()); @@ -738,7 +734,7 @@ public class BlobStoreManagerService extends SystemService { // But if it is a recommit, just leave it as is. if (session.getSessionId() == blob.getBlobId()) { deleteBlobLocked(blob); - userBlobs.remove(blob.getBlobHandle()); + mBlobsMap.remove(blob.getBlobHandle()); } } // Delete redundant data from recommits. @@ -874,13 +870,10 @@ public class BlobStoreManagerService extends SystemService { out.startTag(null, TAG_BLOBS); XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT); - for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); - for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { - out.startTag(null, TAG_BLOB); - userBlobs.valueAt(j).writeToXml(out); - out.endTag(null, TAG_BLOB); - } + for (int i = 0, count = mBlobsMap.size(); i < count; ++i) { + out.startTag(null, TAG_BLOB); + mBlobsMap.valueAt(i).writeToXml(out); + out.endTag(null, TAG_BLOB); } out.endTag(null, TAG_BLOBS); @@ -925,16 +918,21 @@ public class BlobStoreManagerService extends SystemService { if (TAG_BLOB.equals(in.getName())) { final BlobMetadata blobMetadata = BlobMetadata.createFromXml( in, version, mContext); - final SparseArray<String> userPackages = allPackages.get( - blobMetadata.getUserId()); - if (userPackages == null) { - blobMetadata.getBlobFile().delete(); + blobMetadata.removeCommittersFromUnknownPkgs(allPackages); + blobMetadata.removeLeaseesFromUnknownPkgs(allPackages); + mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId()); + if (version >= XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) { + addBlobLocked(blobMetadata); } else { - addBlobForUserLocked(blobMetadata, blobMetadata.getUserId()); - blobMetadata.removeCommittersFromUnknownPkgs(userPackages); - blobMetadata.removeLeaseesFromUnknownPkgs(userPackages); + final BlobMetadata existingBlobMetadata = mBlobsMap.get( + blobMetadata.getBlobHandle()); + if (existingBlobMetadata == null) { + addBlobLocked(blobMetadata); + } else { + existingBlobMetadata.addCommittersAndLeasees(blobMetadata); + blobMetadata.getBlobFile().delete(); + } } - mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId()); } } if (LOGV) { @@ -977,14 +975,6 @@ public class BlobStoreManagerService extends SystemService { } } - private int getPackageUid(String packageName, int userId) { - final int uid = mPackageManagerInternal.getPackageUid( - packageName, - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES, - userId); - return uid; - } - private SparseArray<SparseArray<String>> getAllPackages() { final SparseArray<SparseArray<String>> allPackages = new SparseArray<>(); final int[] allUsers = LocalServices.getService(UserManagerInternal.class).getUserIds(); @@ -1004,7 +994,7 @@ public class BlobStoreManagerService extends SystemService { return allPackages; } - AtomicFile prepareSessionsIndexFile() { + private AtomicFile prepareSessionsIndexFile() { final File file = BlobStoreConfig.prepareSessionIndexFile(); if (file == null) { return null; @@ -1012,7 +1002,7 @@ public class BlobStoreManagerService extends SystemService { return new AtomicFile(file, "session_index" /* commitLogTag */); } - AtomicFile prepareBlobsIndexFile() { + private AtomicFile prepareBlobsIndexFile() { final File file = BlobStoreConfig.prepareBlobsIndexFile(); if (file == null) { return null; @@ -1037,9 +1027,7 @@ public class BlobStoreManagerService extends SystemService { writeBlobSessionsAsync(); // Remove the package from the committer and leasee list - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = - getUserBlobsLocked(UserHandle.getUserId(uid)); - userBlobs.entrySet().removeIf(entry -> { + mBlobsMap.entrySet().removeIf(entry -> { final BlobMetadata blobMetadata = entry.getValue(); final boolean isACommitter = blobMetadata.isACommitter(packageName, uid); if (isACommitter) { @@ -1074,14 +1062,15 @@ public class BlobStoreManagerService extends SystemService { } } - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = - mBlobsMap.removeReturnOld(userId); - if (userBlobs != null) { - for (int i = 0, count = userBlobs.size(); i < count; ++i) { - final BlobMetadata blobMetadata = userBlobs.valueAt(i); + mBlobsMap.entrySet().removeIf(entry -> { + final BlobMetadata blobMetadata = entry.getValue(); + blobMetadata.removeDataForUser(userId); + if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) { deleteBlobLocked(blobMetadata); + return true; } - } + return false; + }); if (LOGV) { Slog.v(TAG, "Removed blobs data in user " + userId); } @@ -1114,22 +1103,19 @@ public class BlobStoreManagerService extends SystemService { } // Cleanup any stale blobs. - for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); - userBlobs.entrySet().removeIf(entry -> { - final BlobMetadata blobMetadata = entry.getValue(); + mBlobsMap.entrySet().removeIf(entry -> { + final BlobMetadata blobMetadata = entry.getValue(); - // Remove expired leases - blobMetadata.removeExpiredLeases(); + // Remove expired leases + blobMetadata.removeExpiredLeases(); - if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) { - deleteBlobLocked(blobMetadata); - deletedBlobIds.add(blobMetadata.getBlobId()); - return true; - } - return false; - }); - } + if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) { + deleteBlobLocked(blobMetadata); + deletedBlobIds.add(blobMetadata.getBlobId()); + return true; + } + return false; + }); writeBlobsInfoAsync(); // Cleanup any stale sessions. @@ -1195,34 +1181,34 @@ public class BlobStoreManagerService extends SystemService { void runClearAllBlobs(@UserIdInt int userId) { synchronized (mBlobsLock) { - for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { - final int blobUserId = mBlobsMap.keyAt(i); - if (userId != UserHandle.USER_ALL && userId != blobUserId) { - continue; + mBlobsMap.entrySet().removeIf(entry -> { + final BlobMetadata blobMetadata = entry.getValue(); + if (userId == UserHandle.USER_ALL) { + mActiveBlobIds.remove(blobMetadata.getBlobId()); + return true; } - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); - for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { - mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId()); + blobMetadata.removeDataForUser(userId); + if (blobMetadata.shouldBeDeleted(false /* respectLeaseWaitTime */)) { + mActiveBlobIds.remove(blobMetadata.getBlobId()); + return true; } - } - if (userId == UserHandle.USER_ALL) { - mBlobsMap.clear(); - } else { - mBlobsMap.remove(userId); - } + return false; + }); writeBlobsInfoAsync(); } } void deleteBlob(@NonNull BlobHandle blobHandle, @UserIdInt int userId) { synchronized (mBlobsLock) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId); - final BlobMetadata blobMetadata = userBlobs.get(blobHandle); + final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle); if (blobMetadata == null) { return; } - deleteBlobLocked(blobMetadata); - userBlobs.remove(blobHandle); + blobMetadata.removeDataForUser(userId); + if (blobMetadata.shouldBeDeleted(false /* respectLeaseWaitTime */)) { + deleteBlobLocked(blobMetadata); + mBlobsMap.remove(blobHandle); + } writeBlobsInfoAsync(); } } @@ -1235,11 +1221,12 @@ public class BlobStoreManagerService extends SystemService { boolean isBlobAvailable(long blobId, int userId) { synchronized (mBlobsLock) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId); - for (BlobMetadata blobMetadata : userBlobs.values()) { - if (blobMetadata.getBlobId() == blobId) { - return true; + for (int i = 0, blobCount = mBlobsMap.size(); i < blobCount; ++i) { + final BlobMetadata blobMetadata = mBlobsMap.valueAt(i); + if (blobMetadata.getBlobId() != blobId) { + continue; } + return blobMetadata.hasACommitterInUser(userId); } return false; } @@ -1274,27 +1261,22 @@ public class BlobStoreManagerService extends SystemService { @GuardedBy("mBlobsLock") private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) { - for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { - final int userId = mBlobsMap.keyAt(i); - if (!dumpArgs.shouldDumpUser(userId)) { + fout.println("List of blobs (" + mBlobsMap.size() + "):"); + fout.increaseIndent(); + for (int i = 0, blobCount = mBlobsMap.size(); i < blobCount; ++i) { + final BlobMetadata blobMetadata = mBlobsMap.valueAt(i); + if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) { continue; } - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); - fout.println("List of blobs in user #" - + userId + " (" + userBlobs.size() + "):"); + fout.println("Blob #" + blobMetadata.getBlobId()); fout.increaseIndent(); - for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { - final BlobMetadata blobMetadata = userBlobs.valueAt(j); - if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) { - continue; - } - fout.println("Blob #" + blobMetadata.getBlobId()); - fout.increaseIndent(); - blobMetadata.dump(fout, dumpArgs); - fout.decreaseIndent(); - } + blobMetadata.dump(fout, dumpArgs); fout.decreaseIndent(); } + if (mBlobsMap.isEmpty()) { + fout.println("<empty>"); + } + fout.decreaseIndent(); } private class BlobStorageStatsAugmenter implements StorageStatsAugmenter { @@ -1308,13 +1290,12 @@ public class BlobStoreManagerService extends SystemService { } }, userId); - forEachBlobInUser(blobMetadata -> { - if (blobMetadata.isALeasee(packageName)) { - if (!blobMetadata.hasOtherLeasees(packageName) || !callerHasStatsPermission) { - blobsDataSize.getAndAdd(blobMetadata.getSize()); - } + forEachBlob(blobMetadata -> { + if (blobMetadata.shouldAttributeToLeasee(packageName, userId, + callerHasStatsPermission)) { + blobsDataSize.getAndAdd(blobMetadata.getSize()); } - }, userId); + }); stats.dataSize += blobsDataSize.get(); } @@ -1330,13 +1311,12 @@ public class BlobStoreManagerService extends SystemService { } }, userId); - forEachBlobInUser(blobMetadata -> { - if (blobMetadata.isALeasee(uid)) { - if (!blobMetadata.hasOtherLeasees(uid) || !callerHasStatsPermission) { - blobsDataSize.getAndAdd(blobMetadata.getSize()); - } + forEachBlob(blobMetadata -> { + if (blobMetadata.shouldAttributeToLeasee(uid, + callerHasStatsPermission)) { + blobsDataSize.getAndAdd(blobMetadata.getSize()); } - }, userId); + }); stats.dataSize += blobsDataSize.get(); } @@ -1352,13 +1332,26 @@ public class BlobStoreManagerService extends SystemService { } } - private void forEachBlobInUser(Consumer<BlobMetadata> consumer, int userId) { - synchronized (mBlobsLock) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId); - for (int i = 0, count = userBlobs.size(); i < count; ++i) { - final BlobMetadata blobMetadata = userBlobs.valueAt(i); - consumer.accept(blobMetadata); - } + private void forEachBlob(Consumer<BlobMetadata> consumer) { + synchronized (mBlobsMap) { + forEachBlobLocked(consumer); + } + } + + @GuardedBy("mBlobsMap") + private void forEachBlobLocked(Consumer<BlobMetadata> consumer) { + for (int blobIdx = 0, count = mBlobsMap.size(); blobIdx < count; ++blobIdx) { + final BlobMetadata blobMetadata = mBlobsMap.valueAt(blobIdx); + consumer.accept(blobMetadata); + } + } + + @GuardedBy("mBlobsMap") + private void forEachBlobLocked(BiConsumer<BlobHandle, BlobMetadata> consumer) { + for (int blobIdx = 0, count = mBlobsMap.size(); blobIdx < count; ++blobIdx) { + final BlobHandle blobHandle = mBlobsMap.keyAt(blobIdx); + final BlobMetadata blobMetadata = mBlobsMap.valueAt(blobIdx); + consumer.accept(blobHandle, blobMetadata); } } @@ -1886,15 +1879,7 @@ public class BlobStoreManagerService extends SystemService { } private int pullBlobData(int atomTag, List<StatsEvent> data) { - synchronized (mBlobsLock) { - for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { - final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); - for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { - final BlobMetadata blob = userBlobs.valueAt(j); - data.add(blob.dumpAsStatsEvent(atomTag)); - } - } - } + forEachBlob(blobMetadata -> data.add(blobMetadata.dumpAsStatsEvent(atomTag))); return StatsManager.PULL_SUCCESS; } diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java index 2c3f682a46e0..3f0032fe537e 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java @@ -56,12 +56,12 @@ import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.Os; import android.util.ExceptionUtils; +import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 88f3df8fa2c4..9ea6f7946fcf 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -208,10 +208,10 @@ public class AlarmManager { public static final int FLAG_PRIORITIZE = 1 << 6; /** - * For apps targeting {@link Build.VERSION_CODES#S} or above, APIs - * {@link #setExactAndAllowWhileIdle(int, long, PendingIntent)} and - * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} will require holding a new - * permission {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM} + * For apps targeting {@link Build.VERSION_CODES#S} or above, any APIs setting exact alarms, + * e.g. {@link #setExact(int, long, PendingIntent)}, + * {@link #setAlarmClock(AlarmClockInfo, PendingIntent)} and others will require holding a new + * permission {@link Manifest.permission#SCHEDULE_EXACT_ALARM} * * @hide */ @@ -219,6 +219,21 @@ public class AlarmManager { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) public static final long REQUIRE_EXACT_ALARM_PERMISSION = 171306433L; + /** + * For apps targeting {@link Build.VERSION_CODES#S} or above, all inexact alarms will require + * to have a minimum window size, expected to be on the order of a few minutes. + * + * Practically, any alarms requiring smaller windows are the same as exact alarms and should use + * the corresponding APIs provided, like {@link #setExact(int, long, PendingIntent)}, et al. + * + * Inexact alarm with shorter windows specified will have their windows elongated by the system. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + public static final long ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS = 185199076L; + @UnsupportedAppUsage private final IAlarmManager mService; private final Context mContext; @@ -483,6 +498,11 @@ public class AlarmManager { * modest timeliness requirements for its alarms. * * <p> + * Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window + * specified is at least a few minutes, as smaller windows are considered practically exact + * and should use the other APIs provided for exact alarms. + * + * <p> * This method can also be used to achieve strict ordering guarantees among * multiple alarms by ensuring that the windows requested for each alarm do * not intersect. @@ -532,6 +552,13 @@ public class AlarmManager { * The OnAlarmListener {@link OnAlarmListener#onAlarm() onAlarm()} method will be * invoked via the specified target Handler, or on the application's main looper * if {@code null} is passed as the {@code targetHandler} parameter. + * + * <p> + * Note: Starting with API {@link Build.VERSION_CODES#S}, the system will ensure that the window + * specified is at least a few minutes, as smaller windows are considered practically exact + * and should use the other APIs provided for exact alarms. + * + * @see #setWindow(int, long, long, PendingIntent) */ public void setWindow(@AlarmType int type, long windowStartMillis, long windowLengthMillis, String tag, OnAlarmListener listener, Handler targetHandler) { diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 7a3614172dff..31da2017bdef 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -452,7 +452,8 @@ public class AlarmManagerService extends SystemService { private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY; - private static final long DEFAULT_MIN_WINDOW = 10_000; + // TODO (b/185199076): Tune based on breakage reports. + private static final long DEFAULT_MIN_WINDOW = 30 * 60 * 1000; private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000; private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000; private static final int DEFAULT_MAX_ALARMS_PER_UID = 500; @@ -1688,12 +1689,22 @@ public class AlarmManagerService extends SystemService { windowLength = AlarmManager.WINDOW_EXACT; } - // Sanity check the window length. This will catch people mistakenly - // trying to pass an end-of-window timestamp rather than a duration. - if (windowLength > AlarmManager.INTERVAL_HALF_DAY) { + // Snap the window to reasonable limits. + if (windowLength > INTERVAL_DAY) { Slog.w(TAG, "Window length " + windowLength - + "ms suspiciously long; limiting to 1 hour"); - windowLength = AlarmManager.INTERVAL_HOUR; + + "ms suspiciously long; limiting to 1 day"); + windowLength = INTERVAL_DAY; + } else if (windowLength > 0 && windowLength < mConstants.MIN_WINDOW) { + if (CompatChanges.isChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, + callingPackage, UserHandle.getUserHandleForUid(callingUid))) { + Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to " + + mConstants.MIN_WINDOW + "ms."); + windowLength = mConstants.MIN_WINDOW; + } else { + // TODO (b/185199076): Remove log once we have some data about what apps will break + Slog.wtf(TAG, "Short window " + windowLength + "ms specified by " + + callingPackage); + } } // Sanity check the recurrence interval. This will catch people who supply @@ -1737,7 +1748,6 @@ public class AlarmManagerService extends SystemService { // Fix this window in place, so that as time approaches we don't collapse it. windowLength = maxElapsed - triggerElapsed; } else { - windowLength = Math.max(windowLength, mConstants.MIN_WINDOW); maxElapsed = triggerElapsed + windowLength; } synchronized (mLock) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 90ece0ba7c61..500735b0b299 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -83,6 +83,7 @@ public final class ConnectivityController extends RestrictingController implemen * instance. */ private static final long MIN_STATS_UPDATE_INTERVAL_MS = 30_000L; + private static final long MIN_ADJUST_CALLBACK_INTERVAL_MS = 1_000L; private static final int UNBYPASSABLE_BG_BLOCKED_REASONS = ~ConnectivityManager.BLOCKED_REASON_NONE; @@ -210,6 +211,7 @@ public final class ConnectivityController extends RestrictingController implemen * is only done in {@link #maybeAdjustRegisteredCallbacksLocked()} and may sometimes be stale. */ private final List<UidStats> mSortedStats = new ArrayList<>(); + private long mLastCallbackAdjustmentTimeElapsed; private static final int MSG_ADJUST_CALLBACKS = 0; @@ -693,7 +695,11 @@ public final class ConnectivityController extends RestrictingController implemen } private void postAdjustCallbacks() { - mHandler.obtainMessage(MSG_ADJUST_CALLBACKS).sendToTarget(); + postAdjustCallbacks(0); + } + + private void postAdjustCallbacks(long delayMs) { + mHandler.sendEmptyMessageDelayed(MSG_ADJUST_CALLBACKS, delayMs); } @GuardedBy("mLock") @@ -708,6 +714,12 @@ public final class ConnectivityController extends RestrictingController implemen } final long nowElapsed = sElapsedRealtimeClock.millis(); + if (nowElapsed - mLastCallbackAdjustmentTimeElapsed < MIN_ADJUST_CALLBACK_INTERVAL_MS) { + postAdjustCallbacks(MIN_ADJUST_CALLBACK_INTERVAL_MS); + return; + } + + mLastCallbackAdjustmentTimeElapsed = nowElapsed; mSortedStats.clear(); for (int u = 0; u < mUidStats.size(); ++u) { @@ -729,17 +741,23 @@ public final class ConnectivityController extends RestrictingController implemen for (int j = 0; j < jobs.size(); ++j) { JobStatus job = jobs.valueAt(j); - us.earliestEnqueueTime = Math.min(us.earliestEnqueueTime, job.enqueueTime); if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_CONNECTIVITY)) { us.numReadyWithConnectivity++; if (isNetworkAvailable(job)) { us.numRequestedNetworkAvailable++; } + // Only use the enqueue time of jobs that would be ready to prevent apps + // from gaming the system (eg. by scheduling a job that requires all + // constraints and has a minimum latency of 6 months to always have the + // earliest enqueue time). + us.earliestEnqueueTime = Math.min(us.earliestEnqueueTime, job.enqueueTime); + if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) { + us.earliestEJEnqueueTime = + Math.min(us.earliestEJEnqueueTime, job.enqueueTime); + } } if (job.shouldTreatAsExpeditedJob() || job.startedAsExpeditedJob) { us.numEJs++; - us.earliestEJEnqueueTime = - Math.min(us.earliestEJEnqueueTime, job.enqueueTime); } else { us.numRegular++; } @@ -920,7 +938,10 @@ public final class ConnectivityController extends RestrictingController implemen UidDefaultNetworkCallback defaultNetworkCallback = mCurrentDefaultNetworkCallbacks.get(jobs.valueAt(0).getSourceUid()); if (defaultNetworkCallback == null) { - maybeRegisterDefaultNetworkCallbackLocked(jobs.valueAt(0)); + // This method is only called via a network callback object. That means something + // changed about a general network characteristic (since we wouldn't be in this + // situation if called from a UID_specific callback). The general network callback + // will handle adjusting the per-UID callbacks, so nothing left to do here. return false; } @@ -1100,8 +1121,13 @@ public final class ConnectivityController extends RestrictingController implemen synchronized (mLock) { if (Objects.equals(mDefaultNetwork, network)) { mDefaultNetwork = null; + updateTrackedJobsLocked(mUid, network); + // Add a delay in case onAvailable()+onBlockedStatusChanged is called for a + // new network. If this onLost was called because the network is completely + // gone, the delay will hel make sure we don't have a short burst of adjusting + // callback calls. + postAdjustCallbacks(1000); } - updateTrackedJobsLocked(mUid, network); } } diff --git a/boot/hiddenapi/OWNERS b/boot/hiddenapi/OWNERS index 5d869fc12ebd..74a3dc05f0da 100644 --- a/boot/hiddenapi/OWNERS +++ b/boot/hiddenapi/OWNERS @@ -1,7 +1,5 @@ # compat-team@ for changes to hiddenapi files -andreionea@google.com -mathewi@google.com -satayev@google.com +file:tools/platform-compat:/OWNERS # Escalations: per-file hiddenapi-* = bdc@google.com, narayan@google.com diff --git a/core/api/current.txt b/core/api/current.txt index 6823f65dd624..4b12d54c5a57 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5666,6 +5666,7 @@ package android.app { field public static final String EXTRA_AUDIO_CONTENTS_URI = "android.audioContents"; field public static final String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri"; field public static final String EXTRA_BIG_TEXT = "android.bigText"; + field public static final String EXTRA_CALL_IS_VIDEO = "android.callIsVideo"; field public static final String EXTRA_CALL_PERSON = "android.callPerson"; field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID"; field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID"; @@ -5972,6 +5973,7 @@ package android.app { method @NonNull public static android.app.Notification.CallStyle forScreeningCall(@NonNull android.app.Person, @NonNull android.app.PendingIntent, @NonNull android.app.PendingIntent); method @NonNull public android.app.Notification.CallStyle setAnswerButtonColorHint(@ColorInt int); method @NonNull public android.app.Notification.CallStyle setDeclineButtonColorHint(@ColorInt int); + method @NonNull public android.app.Notification.CallStyle setIsVideo(boolean); method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon); method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence); } @@ -7130,7 +7132,7 @@ package android.app.admin { method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName); method @NonNull public java.util.List<byte[]> getInstalledCaCerts(@Nullable android.content.ComponentName); method @Nullable public java.util.List<java.lang.String> getKeepUninstalledPackages(@Nullable android.content.ComponentName); - method @NonNull public java.util.Set<java.util.Set<java.lang.String>> getKeyPairGrants(@NonNull String); + method @NonNull public java.util.Map<java.lang.Integer,java.util.Set<java.lang.String>> getKeyPairGrants(@NonNull String); method public int getKeyguardDisabledFeatures(@Nullable android.content.ComponentName); method public int getLockTaskFeatures(@NonNull android.content.ComponentName); method @NonNull public String[] getLockTaskPackages(@NonNull android.content.ComponentName); @@ -11089,7 +11091,6 @@ package android.content { field public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; field public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; - field public static final String ACTION_PACKAGE_FULLY_LOADED = "android.intent.action.PACKAGE_FULLY_LOADED"; field public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED"; field @Deprecated public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; field public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION"; @@ -25216,6 +25217,7 @@ package android.media.session { method public float getPlaybackSpeed(); method public long getPosition(); method public int getState(); + method public boolean isActive(); method public void writeToParcel(android.os.Parcel, int); field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L field public static final long ACTION_PAUSE = 2L; // 0x2L diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index b1e448b7dd77..2be8c9aadcc6 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -161,10 +161,6 @@ package android.media.session { method public void onVolumeChanged(@NonNull android.media.session.MediaSession.Token, int); } - public final class PlaybackState implements android.os.Parcelable { - method public boolean isActiveState(); - } - } package android.net { @@ -236,7 +232,7 @@ package android.net { } public class VpnManager { - field @Deprecated public static final int TYPE_VPN_LEGACY = 3; // 0x3 + field public static final int TYPE_VPN_LEGACY = 3; // 0x3 field public static final int TYPE_VPN_NONE = -1; // 0xffffffff field public static final int TYPE_VPN_OEM = 4; // 0x4 field public static final int TYPE_VPN_PLATFORM = 2; // 0x2 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ce408d1a80f1..199d77a232e9 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -962,6 +962,9 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL"; field public static final String EXTRA_PROVISIONING_TRIGGER = "android.app.extra.PROVISIONING_TRIGGER"; field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; + field public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4 + field public static final int FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1 + field public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2 field public static final int PROVISIONING_TRIGGER_CLOUD_ENROLLMENT = 1; // 0x1 field public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; // 0x4 field @Deprecated public static final int PROVISIONING_TRIGGER_PERSISTENT_DEVICE_OWNER = 3; // 0x3 @@ -976,10 +979,6 @@ package android.app.admin { field public static final int STATE_USER_SETUP_FINALIZED = 3; // 0x3 field public static final int STATE_USER_SETUP_INCOMPLETE = 1; // 0x1 field public static final int STATE_USER_UNMANAGED = 0; // 0x0 - field public static final int SUPPORTED_MODES_DEVICE_OWNER = 4; // 0x4 - field public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; // 0x3 - field public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; // 0x1 - field public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; // 0x2 } public final class SystemUpdatePolicy implements android.os.Parcelable { @@ -1949,6 +1948,8 @@ package android.bluetooth { } public static interface BluetoothAdapter.OobDataCallback { + method public void onError(int); + method public void onOobData(int, @Nullable android.bluetooth.OobData); } public final class BluetoothDevice implements android.os.Parcelable { @@ -8967,7 +8968,7 @@ package android.printservice.recommendation { package android.provider { public class CallLog { - method @RequiresPermission(allOf={android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.INTERACT_ACROSS_USERS}) public static void storeCallComposerPictureAsUser(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>); + method @RequiresPermission(allOf={android.Manifest.permission.WRITE_CALL_LOG, android.Manifest.permission.INTERACT_ACROSS_USERS}) public static void storeCallComposerPicture(@NonNull android.content.Context, @NonNull java.io.InputStream, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.Uri,android.provider.CallLog.CallComposerLoggingException>); } public static class CallLog.CallComposerLoggingException extends java.lang.Throwable { @@ -13627,7 +13628,6 @@ package android.telephony.ims { method public int describeContents(); method @Nullable public String getCallIdParameter(); method @NonNull public byte[] getContent(); - method @Deprecated @NonNull public byte[] getEncodedMessage(); method @NonNull public String getHeaderSection(); method @NonNull public String getStartLine(); method @NonNull public String getViaBranchParameter(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d59e4f8e9c05..0541934fd665 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -263,10 +263,12 @@ package android.app { public class BroadcastOptions { ctor public BroadcastOptions(@NonNull android.os.Bundle); + method public int getMaxManifestReceiverApiLevel(); method public long getTemporaryAppAllowlistDuration(); method @Nullable public String getTemporaryAppAllowlistReason(); method public int getTemporaryAppAllowlistReasonCode(); method public int getTemporaryAppAllowlistType(); + method public void setMaxManifestReceiverApiLevel(int); } public class DownloadManager { @@ -3170,5 +3172,12 @@ package android.window { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction); } + @UiContext public abstract class WindowProviderService extends android.app.Service { + ctor public WindowProviderService(); + method public final void attachToWindowToken(@NonNull android.os.IBinder); + method @Nullable public android.os.Bundle getWindowContextOptions(); + method public abstract int getWindowType(); + } + } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index db42803ac9f9..a24555f79a1c 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4272,7 +4272,8 @@ public class ActivityManager { try { getService().broadcastIntentWithFeature( null, null, intent, null, null, Activity.RESULT_OK, null, null, - null /*permission*/, appOp, null, false, true, userId); + null /*requiredPermissions*/, null /*excludedPermissions*/, appOp, null, false, + true, userId); } catch (RemoteException ex) { } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8ff14b0ad28f..98fee9cf90cf 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4390,11 +4390,12 @@ public final class ActivityThread extends ClientTransactionHandler try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); - ContextImpl context = ContextImpl.createAppContext(this, packageInfo); Application app = packageInfo.makeApplication(false, mInstrumentation); java.lang.ClassLoader cl = packageInfo.getClassLoader(); service = packageInfo.getAppFactory() .instantiateService(cl, data.info.name, data.intent); + final ContextImpl context = ContextImpl.getImpl(service + .createServiceBaseContext(this, packageInfo)); // Service resources must be initialized with the same loaders as the application // context. context.getResources().addLoaders( diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 9da2581e449f..bd7162c1bf3b 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -250,6 +250,7 @@ public class BroadcastOptions { * them. This only applies to receivers declared in the app's AndroidManifest.xml. * @hide */ + @TestApi @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void setMaxManifestReceiverApiLevel(int apiLevel) { mMaxManifestReceiverApiLevel = apiLevel; @@ -259,6 +260,7 @@ public class BroadcastOptions { * Return {@link #setMaxManifestReceiverApiLevel}. * @hide */ + @TestApi @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public int getMaxManifestReceiverApiLevel() { return mMaxManifestReceiverApiLevel; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f7ea3815d567..9753b6748c78 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1176,8 +1176,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, - false, getUserId()); + null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, + AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1194,7 +1194,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - AppOpsManager.OP_NONE, null, false, false, getUserId()); + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1209,7 +1210,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - AppOpsManager.OP_NONE, null, false, false, getUserId()); + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1224,7 +1226,24 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, false, false, + user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions, + String[] excludedPermissions) { + warnIfCallingFromSystemProcess(); + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.prepareToLeaveProcess(this); + ActivityManager.getService().broadcastIntentWithFeature( + mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, + null, Activity.RESULT_OK, null, null, receiverPermissions, excludedPermissions, + AppOpsManager.OP_NONE, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1241,7 +1260,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - AppOpsManager.OP_NONE, options, false, false, getUserId()); + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, + getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1257,8 +1277,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, - false, getUserId()); + null, Activity.RESULT_OK, null, null, receiverPermissions, + null /*excludedPermissions=*/, appOp, null, false, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1275,7 +1295,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - AppOpsManager.OP_NONE, null, true, false, getUserId()); + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, false, + getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1337,8 +1358,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - rd, initialCode, initialData, initialExtras, receiverPermissions, appOp, - options, true, false, getUserId()); + rd, initialCode, initialData, initialExtras, receiverPermissions, + null /*excludedPermissions=*/, appOp, options, true, false, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1351,8 +1372,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, - false, user.getIdentifier()); + null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, + AppOpsManager.OP_NONE, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1375,7 +1396,8 @@ class ContextImpl extends Context { ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, null, Activity.RESULT_OK, null, null, receiverPermissions, - AppOpsManager.OP_NONE, options, false, false, user.getIdentifier()); + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, options, false, false, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1391,8 +1413,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, - false, user.getIdentifier()); + null, Activity.RESULT_OK, null, null, receiverPermissions, + null /*excludedPermissions=*/, appOp, null, false, false, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1442,8 +1464,9 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - rd, initialCode, initialData, initialExtras, receiverPermissions, appOp, - options, true, false, user.getIdentifier()); + rd, initialCode, initialData, initialExtras, receiverPermissions, + null /*excludedPermissions=*/, appOp, options, true, false, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1483,8 +1506,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, - true, getUserId()); + null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, + AppOpsManager.OP_NONE, null, false, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1522,8 +1545,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, - false, true, getUserId()); + null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, + AppOpsManager.OP_NONE, options, false, true, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1558,8 +1581,9 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null, - true, true, getUserId()); + rd, initialCode, initialData, initialExtras, null, + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true, + getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1590,8 +1614,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, - true, user.getIdentifier()); + null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, + AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1605,8 +1629,8 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, - false, true, user.getIdentifier()); + null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/, + AppOpsManager.OP_NONE, options, false, true, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1640,8 +1664,9 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(this); ActivityManager.getService().broadcastIntentWithFeature( mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType, - rd, initialCode, initialData, initialExtras, null, AppOpsManager.OP_NONE, null, - true, true, user.getIdentifier()); + rd, initialCode, initialData, initialExtras, null, + null /*excludedPermissions=*/, AppOpsManager.OP_NONE, null, true, true, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f9279da172a0..89d90a3b9d6f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -137,7 +137,7 @@ interface IActivityManager { int appOp, in Bundle options, boolean serialized, boolean sticky, int userId); int broadcastIntentWithFeature(in IApplicationThread caller, in String callingFeatureId, in Intent intent, in String resolvedType, in IIntentReceiver resultTo, int resultCode, - in String resultData, in Bundle map, in String[] requiredPermissions, + in String resultData, in Bundle map, in String[] requiredPermissions, in String[] excludePermissions, int appOp, in Bundle options, boolean serialized, boolean sticky, int userId); void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId); @UnsupportedAppUsage diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a60f1ca3cdcd..7ce0c7060b05 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1388,6 +1388,12 @@ public class Notification implements Parcelable public static final String EXTRA_CALL_TYPE = "android.callType"; /** + * {@link #extras} key: whether the {@link android.app.Notification.CallStyle} notification + * is for a call that will activate video when answered. This extra is a boolean. + */ + public static final String EXTRA_CALL_IS_VIDEO = "android.callIsVideo"; + + /** * {@link #extras} key: the person to be displayed as calling for the * {@link android.app.Notification.CallStyle} notification. This extra is a {@link Person}. */ @@ -5110,6 +5116,7 @@ public class Notification implements Parcelable TemplateBindResult result) { p.headerless(resId == getBaseLayoutResource() || resId == getHeadsUpBaseLayoutResource() + || resId == getMessagingLayoutResource() || resId == R.layout.notification_template_material_media); RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId); @@ -6308,7 +6315,7 @@ public class Notification implements Parcelable * Gets the theme's background color */ private @ColorInt int getDefaultBackgroundColor() { - return obtainThemeColor(R.attr.colorBackground, + return obtainThemeColor(R.attr.colorSurface, mInNightMode ? Color.BLACK : Color.WHITE); } @@ -6635,6 +6642,10 @@ public class Notification implements Parcelable return R.layout.notification_template_material_messaging; } + private int getBigMessagingLayoutResource() { + return R.layout.notification_template_material_big_messaging; + } + private int getConversationLayoutResource() { return R.layout.notification_template_material_conversation; } @@ -8145,12 +8156,14 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeContentView(boolean increasedHeight) { + // All messaging templates contain the actions ArrayList<Action> originalActions = mBuilder.mActions; - mBuilder.mActions = new ArrayList<>(); - RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */, - false /* hideLargeIcon */); - mBuilder.mActions = originalActions; - return remoteViews; + try { + mBuilder.mActions = new ArrayList<>(); + return makeMessagingView(StandardTemplateParams.VIEW_TYPE_NORMAL); + } finally { + mBuilder.mActions = originalActions; + } } /** @@ -8236,18 +8249,24 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeBigContentView() { - return makeMessagingView(false /* isCollapsed */, true /* hideLargeIcon */); + return makeMessagingView(StandardTemplateParams.VIEW_TYPE_BIG); } /** * Create a messaging layout. * - * @param isCollapsed Should this use the collapsed layout - * @param hideRightIcons Should the reply affordance be shown at the end of the notification + * @param viewType one of StandardTemplateParams.VIEW_TYPE_NORMAL, VIEW_TYPE_BIG, + * VIEW_TYPE_HEADS_UP * @return the created remoteView. */ @NonNull - private RemoteViews makeMessagingView(boolean isCollapsed, boolean hideRightIcons) { + private RemoteViews makeMessagingView(int viewType) { + boolean isCollapsed = viewType != StandardTemplateParams.VIEW_TYPE_BIG; + boolean hideRightIcons = viewType != StandardTemplateParams.VIEW_TYPE_NORMAL; + boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY; + boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT; + boolean isHeaderless = !isConversationLayout && isCollapsed; + CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle) ? super.mBigContentTitle : mConversationTitle; @@ -8265,23 +8284,26 @@ public class Notification implements Parcelable } else { isOneToOne = !isGroupConversation(); } - boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY; - boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT; + if (isHeaderless && isOneToOne && TextUtils.isEmpty(conversationTitle)) { + conversationTitle = getOtherPersonName(); + } + Icon largeIcon = mBuilder.mN.mLargeIcon; TemplateBindResult bindResult = new TemplateBindResult(); StandardTemplateParams p = mBuilder.mParams.reset() - .viewType(isCollapsed ? StandardTemplateParams.VIEW_TYPE_NORMAL - : StandardTemplateParams.VIEW_TYPE_BIG) + .viewType(viewType) .highlightExpander(isConversationLayout) .hideProgress(true) - .title(conversationTitle) + .title(isHeaderless ? conversationTitle : null) .text(null) .hideLargeIcon(hideRightIcons || isOneToOne) - .headerTextSecondary(conversationTitle); + .headerTextSecondary(isHeaderless ? null : conversationTitle); RemoteViews contentView = mBuilder.applyStandardTemplateWithActions( isConversationLayout ? mBuilder.getConversationLayoutResource() - : mBuilder.getMessagingLayoutResource(), + : isCollapsed + ? mBuilder.getMessagingLayoutResource() + : mBuilder.getBigMessagingLayoutResource(), p, bindResult); if (isConversationLayout) { @@ -8290,14 +8312,6 @@ public class Notification implements Parcelable } addExtras(mBuilder.mN.extras); - if (!isConversationLayout) { - // also update the end margin if there is an image - // NOTE: This template doesn't support moving this icon to the left, so we don't - // need to fully apply the MarginSet - contentView.setViewLayoutMargin(R.id.notification_messaging, RemoteViews.MARGIN_END, - bindResult.mHeadingExtraMarginSet.getDpValue(), - TypedValue.COMPLEX_UNIT_DIP); - } contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.getSmallIconColor(p)); contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor", @@ -8323,6 +8337,10 @@ public class Notification implements Parcelable contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsImportantConversation", isImportantConversation); } + if (isHeaderless) { + // Collapsed legacy messaging style has a 1-line limit. + contentView.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); + } contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon", largeIcon); contentView.setBundle(R.id.status_bar_latest_event_content, "setData", @@ -8330,6 +8348,22 @@ public class Notification implements Parcelable return contentView; } + private CharSequence getKey(Person person) { + return person == null ? null + : person.getKey() == null ? person.getName() : person.getKey(); + } + + private CharSequence getOtherPersonName() { + CharSequence userKey = getKey(mUser); + for (int i = mMessages.size() - 1; i >= 0; i--) { + Person sender = mMessages.get(i).getSenderPerson(); + if (sender != null && !TextUtils.equals(userKey, getKey(sender))) { + return sender.getName(); + } + } + return null; + } + private boolean hasOnlyWhiteSpaceSenders() { for (int i = 0; i < mMessages.size(); i++) { Message m = mMessages.get(i); @@ -8364,12 +8398,7 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { - RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */, - true /* hideLargeIcon */); - if (mConversationType == CONVERSATION_TYPE_LEGACY) { - remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1); - } - return remoteViews; + return makeMessagingView(StandardTemplateParams.VIEW_TYPE_HEADS_UP); } public static final class Message { @@ -9146,6 +9175,7 @@ public class Notification implements Parcelable private PendingIntent mAnswerIntent; private PendingIntent mDeclineIntent; private PendingIntent mHangUpIntent; + private boolean mIsVideo; private Integer mAnswerButtonColor; private Integer mDeclineButtonColor; private Icon mVerificationIcon; @@ -9238,6 +9268,16 @@ public class Notification implements Parcelable } /** + * Sets whether the call is a video call, which may affect the icons or text used on the + * required action buttons. + */ + @NonNull + public CallStyle setIsVideo(boolean isVideo) { + mIsVideo = isVideo; + return this; + } + + /** * Optional icon to be displayed with {@link #setVerificationText(CharSequence) text} * as a verification status of the caller. */ @@ -9365,8 +9405,10 @@ public class Notification implements Parcelable @Nullable private Action makeAnswerAction() { - return mAnswerIntent == null ? null : makeAction(R.drawable.ic_call_answer, - R.string.call_notification_answer_action, + return mAnswerIntent == null ? null : makeAction( + mIsVideo ? R.drawable.ic_call_answer_video : R.drawable.ic_call_answer, + mIsVideo ? R.string.call_notification_answer_video_action + : R.string.call_notification_answer_action, mAnswerButtonColor, R.color.call_notification_answer_color, mAnswerIntent); } @@ -9545,6 +9587,7 @@ public class Notification implements Parcelable public void addExtras(Bundle extras) { super.addExtras(extras); extras.putInt(EXTRA_CALL_TYPE, mCallType); + extras.putBoolean(EXTRA_CALL_IS_VIDEO, mIsVideo); extras.putParcelable(EXTRA_CALL_PERSON, mPerson); if (mVerificationIcon != null) { extras.putParcelable(EXTRA_VERIFICATION_ICON, mVerificationIcon); @@ -9587,6 +9630,7 @@ public class Notification implements Parcelable protected void restoreFromExtras(Bundle extras) { super.restoreFromExtras(extras); mCallType = extras.getInt(EXTRA_CALL_TYPE); + mIsVideo = extras.getBoolean(EXTRA_CALL_IS_VIDEO); mPerson = extras.getParcelable(EXTRA_CALL_PERSON); mVerificationIcon = extras.getParcelable(EXTRA_VERIFICATION_ICON); mVerificationText = extras.getCharSequence(EXTRA_VERIFICATION_TEXT); diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 2ceea7f1a6a8..0ab3f2f4be46 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -861,6 +861,19 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac } /** + * Creates the base {@link Context} of this {@link Service}. + * Users may override this API to create customized base context. + * + * @see android.window.WindowProviderService WindowProviderService class for example + * @see ContextWrapper#attachBaseContext(Context) + * + * @hide + */ + public Context createServiceBaseContext(ActivityThread mainThread, LoadedApk packageInfo) { + return ContextImpl.createAppContext(mainThread, packageInfo); + } + + /** * @hide * Clean up any references to avoid leaks. */ diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7af9482b0010..4dc044208a9f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -119,6 +119,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -1214,15 +1215,14 @@ public class DevicePolicyManager { public @interface ProvisioningTrigger {} /** - * Possible values for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES}. + * Flags for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES}. * * @hide */ - @IntDef(prefix = { "SUPPORTED_MODES_" }, value = { - SUPPORTED_MODES_ORGANIZATION_OWNED, - SUPPORTED_MODES_PERSONALLY_OWNED, - SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED, - SUPPORTED_MODES_DEVICE_OWNER + @IntDef(flag = true, prefix = { "FLAG_SUPPORTED_MODES_" }, value = { + FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED, + FLAG_SUPPORTED_MODES_PERSONALLY_OWNED, + FLAG_SUPPORTED_MODES_DEVICE_OWNER }) @Retention(RetentionPolicy.SOURCE) public @interface ProvisioningConfiguration {} @@ -1307,7 +1307,7 @@ public class DevicePolicyManager { public static final int PROVISIONING_TRIGGER_MANAGED_ACCOUNT = 4; /** - * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is + * Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is * organization-owned. * * <p>Using this value indicates the admin app can only be provisioned in either a @@ -1316,55 +1316,48 @@ public class DevicePolicyManager { * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link * #PROVISIONING_MODE_MANAGED_PROFILE} and {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}. * - * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity + * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras. * + * <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}. In + * that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have + * the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link + * #PROVISIONING_MODE_MANAGED_PROFILE}, {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and + * {@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}. + * * @hide */ @SystemApi - public static final int SUPPORTED_MODES_ORGANIZATION_OWNED = 1; + public static final int FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED = 1; /** - * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning is - * personally-owned. + * Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning + * is personally-owned. * - * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} + * <p>Using this flag will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra * contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}. * - * @hide - */ - @SystemApi - public static final int SUPPORTED_MODES_PERSONALLY_OWNED = 2; - - /** - * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that provisioning could - * be organization-owned or personally-owned. - * - * <p>Using this value will cause the admin app's {@link #ACTION_GET_PROVISIONING_MODE} - * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra - * contain {@link + * <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}. In + * that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have the + * {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link * #PROVISIONING_MODE_MANAGED_PROFILE}, {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE} and * {@link #PROVISIONING_MODE_MANAGED_PROFILE_ON_PERSONAL_DEVICE}. * - * <p>Also, if this value is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity - * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link - * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras. - * * @hide */ @SystemApi - public static final int SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED = 3; + public static final int FLAG_SUPPORTED_MODES_PERSONALLY_OWNED = 1 << 1; /** - * A value for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that the only supported - * provisioning mode is device owner. + * Flag for {@link #EXTRA_PROVISIONING_SUPPORTED_MODES} indicating that the only + * supported provisioning mode is device owner. * * @hide */ @SystemApi - public static final int SUPPORTED_MODES_DEVICE_OWNER = 4; + public static final int FLAG_SUPPORTED_MODES_DEVICE_OWNER = 1 << 2; /** * This MIME type is used for starting the device owner provisioning. @@ -2637,7 +2630,7 @@ public class DevicePolicyManager { * An integer extra indication what provisioning modes should be available for the admin app * to pick. * - * <p>The default value is {@link #SUPPORTED_MODES_ORGANIZATION_OWNED}. + * <p>The default value is {@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}. * * <p>The value of this extra will determine the contents of the {@link * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array that is passed to the admin app as an @@ -2648,13 +2641,21 @@ public class DevicePolicyManager { * #ACTION_GET_PROVISIONING_MODE} activity via the {@link #EXTRA_PROVISIONING_IMEI} and {@link * #EXTRA_PROVISIONING_SERIAL_NUMBER} respectively. * + * <p>The allowed flag combinations are: + * <ul> + * <li>{@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}</li> + * <li>{@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}</li> + * <li>{@link #FLAG_SUPPORTED_MODES_DEVICE_OWNER}</li> + * <li>{@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED} + * | {@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}</li> + * </ul> + * * <p>This extra is only respected when provided alongside the {@link * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent action. * - * @see #SUPPORTED_MODES_ORGANIZATION_OWNED - * @see #SUPPORTED_MODES_PERSONALLY_OWNED - * @see #SUPPORTED_MODES_ORGANIZATION_AND_PERSONALLY_OWNED - * @see #SUPPORTED_MODES_DEVICE_OWNER + * @see #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED + * @see #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED + * @see #FLAG_SUPPORTED_MODES_DEVICE_OWNER * @hide */ @SystemApi @@ -6460,12 +6461,14 @@ public class DevicePolicyManager { * to a given KeyChain key. * * Key are granted on a per-UID basis, so if several apps share the same UID, granting access to - * one of them automatically grants it to others. This method returns a set of sets of package - * names, where each internal set contains all packages sharing the same UID. Grantee packages - * that don't share UID with other packages are represented by singleton sets. + * one of them automatically grants it to others. This method returns a map containing one entry + * per grantee UID. Entries have UIDs as keys and sets of corresponding package names as values. + * In particular, grantee packages that don't share UID with other packages are represented by + * entries having singleton sets as values. * * @param alias The alias of the key to grant access to. - * @return package names of apps that have access to a given key, grouped by UIDs + * @return apps that have access to a given key, arranged in a map from UID to sets of + * package names. * * @throws SecurityException if the caller is not a device owner, a profile owner or * delegated certificate chooser. @@ -6473,26 +6476,11 @@ public class DevicePolicyManager { * * @see #grantKeyPairToApp(ComponentName, String, String) */ - public @NonNull Set<Set<String>> getKeyPairGrants(@NonNull String alias) { + public @NonNull Map<Integer, Set<String>> getKeyPairGrants(@NonNull String alias) { throwIfParentInstance("getKeyPairGrants"); try { - // Set of sets is flattened into a null-separated list. - final List<String> flattened = - mService.getKeyPairGrants(mContext.getPackageName(), alias); - final Set<Set<String>> result = new HashSet<>(); - Set<String> pkgsForOneUid = new HashSet<>(); - for (final String pkg : flattened) { - if (pkg == null) { - result.add(pkgsForOneUid); - pkgsForOneUid = new HashSet<>(); - } else { - pkgsForOneUid.add(pkg); - } - } - if (!pkgsForOneUid.isEmpty()) { - result.add(pkgsForOneUid); - } - return result; + // The result is wrapped into intermediate parcelable representation. + return mService.getKeyPairGrants(mContext.getPackageName(), alias).getPackagesByUid(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 05b0be4c5813..8e86f6545f23 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -20,6 +20,7 @@ package android.app.admin; import android.app.admin.NetworkEvent; import android.app.IApplicationThread; import android.app.IServiceConnection; +import android.app.admin.ParcelableGranteeMap; import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; @@ -485,7 +486,7 @@ interface IDevicePolicyManager { boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags); boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant); - List<String> getKeyPairGrants(in String callerPackage, in String alias); + ParcelableGranteeMap getKeyPairGrants(in String callerPackage, in String alias); boolean setKeyGrantToWifiAuth(String callerPackage, String alias, boolean hasGrant); boolean isKeyPairGrantedToWifiAuth(String callerPackage, String alias); diff --git a/core/java/android/app/admin/ParcelableGranteeMap.aidl b/core/java/android/app/admin/ParcelableGranteeMap.aidl new file mode 100644 index 000000000000..cd15b4901472 --- /dev/null +++ b/core/java/android/app/admin/ParcelableGranteeMap.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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.app.admin; + +parcelable ParcelableGranteeMap;
\ No newline at end of file diff --git a/core/java/android/app/admin/ParcelableGranteeMap.java b/core/java/android/app/admin/ParcelableGranteeMap.java new file mode 100644 index 000000000000..be348ad067c9 --- /dev/null +++ b/core/java/android/app/admin/ParcelableGranteeMap.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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.app.admin; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.ArraySet; + +import java.util.Map; +import java.util.Set; + +/** + * Class for marshalling keypair grantees for a given KeyChain key via Binder. + * + * @hide + */ +public class ParcelableGranteeMap implements Parcelable { + + private final Map<Integer, Set<String>> mPackagesByUid; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mPackagesByUid.size()); + for (final Map.Entry<Integer, Set<String>> uidEntry : mPackagesByUid.entrySet()) { + dest.writeInt(uidEntry.getKey()); + dest.writeStringArray(uidEntry.getValue().toArray(new String[0])); + } + } + + public static final @NonNull Parcelable.Creator<ParcelableGranteeMap> CREATOR = + new Parcelable.Creator<ParcelableGranteeMap>() { + @Override + public ParcelableGranteeMap createFromParcel(Parcel source) { + final Map<Integer, Set<String>> packagesByUid = new ArrayMap<>(); + final int numUids = source.readInt(); + for (int i = 0; i < numUids; i++) { + final int uid = source.readInt(); + final String[] pkgs = source.readStringArray(); + packagesByUid.put(uid, new ArraySet<>(pkgs)); + } + return new ParcelableGranteeMap(packagesByUid); + } + + @Override + public ParcelableGranteeMap[] newArray(int size) { + return new ParcelableGranteeMap[size]; + } + }; + + /** + * Creates an instance holding a reference (not a copy) to the given map. + */ + public ParcelableGranteeMap(@NonNull Map<Integer, Set<String>> packagesByUid) { + mPackagesByUid = packagesByUid; + } + + /** + * Returns a reference (not a copy) to the stored map. + */ + @NonNull + public Map<Integer, Set<String>> getPackagesByUid() { + return mPackagesByUid; + } +} diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 2a50e0de2145..eb4c624be0b2 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -65,6 +65,7 @@ interface IUsageStatsManager { void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs, String callingPackage); void reportUsageStop(in IBinder activity, String token, String callingPackage); + void reportUserInteraction(String packageName, int userId); int getUsageSource(); void forceUsageSourceSettingRead(); long getLastTimeAnyComponentUsed(String packageName); diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 542473a3d2a9..e8175c709d85 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -982,6 +982,20 @@ public final class UsageStatsManager { } /** + * Reports user interaction with a given package in the given user. + * + * <p><em>This method is only for use by the system</em> + * @hide + */ + public void reportUserInteraction(@NonNull String packageName, int userId) { + try { + mService.reportUserInteraction(packageName, userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Report usage associated with a particular {@code token} has started. Tokens are app defined * strings used to represent usage of in-app features. Apps with the {@link * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 79fd8072f9f0..236185e2a28d 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3095,8 +3095,6 @@ public final class BluetoothAdapter { * * @param transport - whether the {@link OobData} is generated for LE or Classic. * @param oobData - data generated in the host stack(LE) or controller (Classic) - * - * @hide */ void onOobData(@Transport int transport, @Nullable OobData oobData); @@ -3104,8 +3102,6 @@ public final class BluetoothAdapter { * Provides feedback when things don't go as expected. * * @param errorCode - the code descibing the type of error that occurred. - * - * @hide */ void onError(@OobError int errorCode); } diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java index 08d694eb93e2..d6868e0ffd5c 100644 --- a/core/java/android/bluetooth/OobData.java +++ b/core/java/android/bluetooth/OobData.java @@ -830,7 +830,7 @@ public final class OobData implements Parcelable { @Nullable @SystemApi public byte[] getLeAppearance() { - return mLeTemporaryKey; + return mLeAppearance; } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 09ac8103c526..318913f3bec0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2208,6 +2208,17 @@ public abstract class Context { } /** + * Like {@link #sendBroadcastMultiplePermissions(Intent, String[])}, but also allows + * specification of a list of excluded permissions. This allows sending a broadcast to an + * app that has the permissions in `receiverPermissions` but not `excludedPermissions`. + * @hide + */ + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Broadcast the given intent to all interested BroadcastReceivers, allowing * an array of required permissions to be enforced. This call is asynchronous; it returns * immediately, and you will continue executing while the receivers are run. No results are diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 8936d0c47a58..dddcbea63872 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -493,6 +493,13 @@ public class ContextWrapper extends Context { /** @hide */ @Override + public void sendBroadcastMultiplePermissions(@NonNull Intent intent, + @NonNull String[] receiverPermissions, @Nullable String[] excludedPermissions) { + mBase.sendBroadcastMultiplePermissions(intent, receiverPermissions, excludedPermissions); + } + + /** @hide */ + @Override public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user, String[] receiverPermissions) { mBase.sendBroadcastAsUserMultiplePermissions(intent, user, receiverPermissions); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9ad017c5f9c6..f8d407db07a5 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2871,6 +2871,7 @@ public class Intent implements Parcelable, Cloneable { * </ul> * * <p class="note">This is a protected intent that can only be sent by the system. + * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_FULLY_LOADED = diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java index 3a4aae19d459..4ec2e73a0b83 100644 --- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java +++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java @@ -40,7 +40,7 @@ public class ParsedAttribution implements Parcelable { public static final int MAX_ATTRIBUTION_TAG_LEN = 50; /** Maximum amount of attributions per package */ - private static final int MAX_NUM_ATTRIBUTIONS = 1000; + private static final int MAX_NUM_ATTRIBUTIONS = 10000; /** Tag of the attribution */ public final @NonNull String tag; @@ -100,7 +100,7 @@ public class ParsedAttribution implements Parcelable { - // Code below generated by codegen v1.0.22. + // Code below generated by codegen v1.0.23. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code @@ -215,8 +215,8 @@ public class ParsedAttribution implements Parcelable { }; @DataClass.Generated( - time = 1607463855175L, - codegenVersion = "1.0.22", + time = 1618351459610L, + codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java", inputSignatures = "public static final int MAX_ATTRIBUTION_TAG_LEN\nprivate static final int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\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.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)") @Deprecated diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java index adc668f8fa02..77bd14756637 100644 --- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java +++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java @@ -32,7 +32,9 @@ import android.os.UserHandle; import com.android.internal.util.CollectionUtils; +import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -350,6 +352,8 @@ public final class DomainVerificationManager { * * The set will be ordered from lowest to highest priority. * + * @param domain The host to query for. An invalid domain will result in an empty set. + * * @hide */ @SystemApi @@ -357,11 +361,11 @@ public final class DomainVerificationManager { @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public SortedSet<DomainOwner> getOwnersForDomain(@NonNull String domain) { try { + Objects.requireNonNull(domain); final List<DomainOwner> orderedList = mDomainVerificationManager.getOwnersForDomain( domain, mContext.getUserId()); SortedSet<DomainOwner> set = new TreeSet<>( - (first, second) -> Integer.compare(orderedList.indexOf(first), - orderedList.indexOf(second))); + Comparator.comparingInt(orderedList::indexOf)); set.addAll(orderedList); return set; } catch (RemoteException e) { diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl index 62d727c080e3..1268658d75f5 100644 --- a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl +++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl @@ -16,11 +16,9 @@ package android.hardware.biometrics; -import android.hardware.biometrics.BiometricSourceType; - /** * @hide */ oneway interface IBiometricEnabledOnKeyguardCallback { - void onChanged(in BiometricSourceType type, boolean enabled, int userId); + void onChanged(boolean enabled, int userId); }
\ No newline at end of file diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index 5f65d46f3b1e..662ebb356f4c 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -78,10 +78,8 @@ public class VpnManager { /** * An IPsec VPN created by the built-in LegacyVpnRunner. - * @deprecated new Android devices should use VPN_TYPE_PLATFORM instead. * @hide */ - @Deprecated @SystemApi(client = MODULE_LIBRARIES) public static final int TYPE_VPN_LEGACY = 3; @@ -418,4 +416,4 @@ public class VpnManager { throw e.rethrowFromSystemServer(); } } -}
\ No newline at end of file +} diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index 75db3820f5e7..d7c6fa1822b9 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -79,7 +79,12 @@ public final class VcnGatewayConnectionConfig { @VisibleForTesting(visibility = Visibility.PRIVATE) static final int MIN_MTU_V6 = 1280; - private static final Set<Integer> ALLOWED_CAPABILITIES; + /** + * The set of allowed capabilities for exposed capabilities. + * + * @hide + */ + public static final Set<Integer> ALLOWED_CAPABILITIES; static { Set<Integer> allowedCaps = new ArraySet<>(); diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index 033148379041..ba63ba4521df 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -36,7 +36,7 @@ public abstract class BatteryConsumer { * @hide */ @IntDef(prefix = {"POWER_COMPONENT_"}, value = { - POWER_COMPONENT_USAGE, + POWER_COMPONENT_SCREEN, POWER_COMPONENT_CPU, POWER_COMPONENT_BLUETOOTH, POWER_COMPONENT_CAMERA, @@ -49,14 +49,16 @@ public abstract class BatteryConsumer { POWER_COMPONENT_GNSS, POWER_COMPONENT_WIFI, POWER_COMPONENT_WAKELOCK, - POWER_COMPONENT_SCREEN, + POWER_COMPONENT_MEMORY, + POWER_COMPONENT_PHONE, + POWER_COMPONENT_IDLE, POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, }) @Retention(RetentionPolicy.SOURCE) public static @interface PowerComponent { } - public static final int POWER_COMPONENT_USAGE = 0; + public static final int POWER_COMPONENT_SCREEN = 0; public static final int POWER_COMPONENT_CPU = 1; public static final int POWER_COMPONENT_BLUETOOTH = 2; public static final int POWER_COMPONENT_CAMERA = 3; @@ -69,13 +71,15 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_GNSS = 10; public static final int POWER_COMPONENT_WIFI = 11; public static final int POWER_COMPONENT_WAKELOCK = 12; - public static final int POWER_COMPONENT_SCREEN = 13; + public static final int POWER_COMPONENT_MEMORY = 13; + public static final int POWER_COMPONENT_PHONE = 13; + public static final int POWER_COMPONENT_IDLE = 15; // Power that is re-attributed to other battery consumers. For example, for System Server // this represents the power attributed to apps requesting system services. // The value should be negative or zero. - public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 14; + public static final int POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS = 16; - public static final int POWER_COMPONENT_COUNT = 15; + public static final int POWER_COMPONENT_COUNT = 17; public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999; @@ -87,7 +91,7 @@ public abstract class BatteryConsumer { * @hide */ @IntDef(prefix = {"TIME_COMPONENT_"}, value = { - TIME_COMPONENT_USAGE, + TIME_COMPONENT_SCREEN, TIME_COMPONENT_CPU, TIME_COMPONENT_CPU_FOREGROUND, TIME_COMPONENT_BLUETOOTH, @@ -98,13 +102,15 @@ public abstract class BatteryConsumer { TIME_COMPONENT_GNSS, TIME_COMPONENT_WIFI, TIME_COMPONENT_WAKELOCK, - TIME_COMPONENT_SCREEN, + TIME_COMPONENT_MEMORY, + TIME_COMPONENT_PHONE, + TIME_COMPONENT_IDLE, }) @Retention(RetentionPolicy.SOURCE) public static @interface TimeComponent { } - public static final int TIME_COMPONENT_USAGE = 0; + public static final int TIME_COMPONENT_SCREEN = 0; public static final int TIME_COMPONENT_CPU = 1; public static final int TIME_COMPONENT_CPU_FOREGROUND = 2; public static final int TIME_COMPONENT_BLUETOOTH = 3; @@ -117,9 +123,11 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_GNSS = 10; public static final int TIME_COMPONENT_WIFI = 11; public static final int TIME_COMPONENT_WAKELOCK = 12; - public static final int TIME_COMPONENT_SCREEN = 13; + public static final int TIME_COMPONENT_MEMORY = 13; + public static final int TIME_COMPONENT_PHONE = 14; + public static final int TIME_COMPONENT_IDLE = 15; - public static final int TIME_COMPONENT_COUNT = 14; + public static final int TIME_COMPONENT_COUNT = 16; public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000; public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999; @@ -148,7 +156,7 @@ public abstract class BatteryConsumer { */ public static final int POWER_MODEL_MEASURED_ENERGY = 1; - private final PowerComponents mPowerComponents; + protected final PowerComponents mPowerComponents; protected BatteryConsumer(@NonNull PowerComponents powerComponents) { mPowerComponents = powerComponents; @@ -192,6 +200,23 @@ public abstract class BatteryConsumer { return mPowerComponents.getConsumedPowerForCustomComponent(componentId); } + public int getCustomPowerComponentCount() { + return mPowerComponents.getCustomPowerComponentCount(); + } + + void setCustomPowerComponentNames(String[] customPowerComponentNames) { + mPowerComponents.setCustomPowerComponentNames(customPowerComponentNames); + } + + /** + * Returns the name of the specified power component. + * + * @param componentId The ID of the custom power component. + */ + public String getCustomPowerComponentName(int componentId) { + return mPowerComponents.getCustomPowerComponentName(componentId); + } + /** * Returns the amount of time since BatteryStats reset used by the specified component, e.g. * CPU, WiFi etc. @@ -222,9 +247,9 @@ public abstract class BatteryConsumer { protected abstract static class BaseBuilder<T extends BaseBuilder<?>> { final PowerComponents.Builder mPowerComponentsBuilder; - public BaseBuilder(int customPowerComponentCount, int customTimeComponentCount, - boolean includePowerModels) { - mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentCount, + public BaseBuilder(@NonNull String[] customPowerComponentNames, + int customTimeComponentCount, boolean includePowerModels) { + mPowerComponentsBuilder = new PowerComponents.Builder(customPowerComponentNames, customTimeComponentCount, includePowerModels); } diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index f2887748f460..8ea59ce37018 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -39,9 +39,10 @@ public final class BatteryUsageStats implements Parcelable { private final double mDischargedPowerUpperBound; private final long mBatteryTimeRemainingMs; private final long mChargeTimeRemainingMs; - private final ArrayList<UidBatteryConsumer> mUidBatteryConsumers; - private final ArrayList<SystemBatteryConsumer> mSystemBatteryConsumers; - private final ArrayList<UserBatteryConsumer> mUserBatteryConsumers; + private final String[] mCustomPowerComponentNames; + private final List<UidBatteryConsumer> mUidBatteryConsumers; + private final List<SystemBatteryConsumer> mSystemBatteryConsumers; + private final List<UserBatteryConsumer> mUserBatteryConsumers; private final Parcel mHistoryBuffer; private final List<BatteryStats.HistoryTag> mHistoryTagPool; @@ -54,6 +55,7 @@ public final class BatteryUsageStats implements Parcelable { mHistoryTagPool = builder.mHistoryTagPool; mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs; mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs; + mCustomPowerComponentNames = builder.mCustomPowerComponentNames; double totalPower = 0; @@ -182,12 +184,31 @@ public final class BatteryUsageStats implements Parcelable { mDischargedPowerUpperBound = source.readDouble(); mBatteryTimeRemainingMs = source.readLong(); mChargeTimeRemainingMs = source.readLong(); - mUidBatteryConsumers = new ArrayList<>(); - source.readParcelableList(mUidBatteryConsumers, getClass().getClassLoader()); - mSystemBatteryConsumers = new ArrayList<>(); - source.readParcelableList(mSystemBatteryConsumers, getClass().getClassLoader()); - mUserBatteryConsumers = new ArrayList<>(); - source.readParcelableList(mUserBatteryConsumers, getClass().getClassLoader()); + mCustomPowerComponentNames = source.readStringArray(); + int uidCount = source.readInt(); + mUidBatteryConsumers = new ArrayList<>(uidCount); + for (int i = 0; i < uidCount; i++) { + final UidBatteryConsumer consumer = + UidBatteryConsumer.CREATOR.createFromParcel(source); + consumer.setCustomPowerComponentNames(mCustomPowerComponentNames); + mUidBatteryConsumers.add(consumer); + } + int sysCount = source.readInt(); + mSystemBatteryConsumers = new ArrayList<>(sysCount); + for (int i = 0; i < sysCount; i++) { + final SystemBatteryConsumer consumer = + SystemBatteryConsumer.CREATOR.createFromParcel(source); + consumer.setCustomPowerComponentNames(mCustomPowerComponentNames); + mSystemBatteryConsumers.add(consumer); + } + int userCount = source.readInt(); + mUserBatteryConsumers = new ArrayList<>(userCount); + for (int i = 0; i < userCount; i++) { + final UserBatteryConsumer consumer = + UserBatteryConsumer.CREATOR.createFromParcel(source); + consumer.setCustomPowerComponentNames(mCustomPowerComponentNames); + mUserBatteryConsumers.add(consumer); + } if (source.readBoolean()) { mHistoryBuffer = Parcel.obtain(); mHistoryBuffer.setDataSize(0); @@ -222,9 +243,19 @@ public final class BatteryUsageStats implements Parcelable { dest.writeDouble(mDischargedPowerUpperBound); dest.writeLong(mBatteryTimeRemainingMs); dest.writeLong(mChargeTimeRemainingMs); - dest.writeParcelableList(mUidBatteryConsumers, flags); - dest.writeParcelableList(mSystemBatteryConsumers, flags); - dest.writeParcelableList(mUserBatteryConsumers, flags); + dest.writeStringArray(mCustomPowerComponentNames); + dest.writeInt(mUidBatteryConsumers.size()); + for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) { + mUidBatteryConsumers.get(i).writeToParcel(dest, flags); + } + dest.writeInt(mSystemBatteryConsumers.size()); + for (int i = mSystemBatteryConsumers.size() - 1; i >= 0; i--) { + mSystemBatteryConsumers.get(i).writeToParcel(dest, flags); + } + dest.writeInt(mUserBatteryConsumers.size()); + for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) { + mUserBatteryConsumers.get(i).writeToParcel(dest, flags); + } if (mHistoryBuffer != null) { dest.writeBoolean(true); @@ -259,7 +290,8 @@ public final class BatteryUsageStats implements Parcelable { * Builder for BatteryUsageStats. */ public static final class Builder { - private final int mCustomPowerComponentCount; + @NonNull + private final String[] mCustomPowerComponentNames; private final int mCustomTimeComponentCount; private final boolean mIncludePowerModels; private long mStatsStartTimestampMs; @@ -277,13 +309,13 @@ public final class BatteryUsageStats implements Parcelable { private Parcel mHistoryBuffer; private List<BatteryStats.HistoryTag> mHistoryTagPool; - public Builder(int customPowerComponentCount, int customTimeComponentCount) { - this(customPowerComponentCount, customTimeComponentCount, false); + public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount) { + this(customPowerComponentNames, customTimeComponentCount, false); } - public Builder(int customPowerComponentCount, int customTimeComponentCount, + public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, boolean includePowerModels) { - mCustomPowerComponentCount = customPowerComponentCount; + mCustomPowerComponentNames = customPowerComponentNames; mCustomTimeComponentCount = customTimeComponentCount; mIncludePowerModels = includePowerModels; } @@ -366,7 +398,7 @@ public final class BatteryUsageStats implements Parcelable { int uid = batteryStatsUid.getUid(); UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid); if (builder == null) { - builder = new UidBatteryConsumer.Builder(mCustomPowerComponentCount, + builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames, mCustomTimeComponentCount, mIncludePowerModels, batteryStatsUid); mUidBatteryConsumerBuilders.put(uid, builder); } @@ -382,7 +414,7 @@ public final class BatteryUsageStats implements Parcelable { @SystemBatteryConsumer.DrainType int drainType) { SystemBatteryConsumer.Builder builder = mSystemBatteryConsumerBuilders.get(drainType); if (builder == null) { - builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentCount, + builder = new SystemBatteryConsumer.Builder(mCustomPowerComponentNames, mCustomTimeComponentCount, mIncludePowerModels, drainType); mSystemBatteryConsumerBuilders.put(drainType, builder); } @@ -397,7 +429,7 @@ public final class BatteryUsageStats implements Parcelable { public UserBatteryConsumer.Builder getOrCreateUserBatteryConsumerBuilder(int userId) { UserBatteryConsumer.Builder builder = mUserBatteryConsumerBuilders.get(userId); if (builder == null) { - builder = new UserBatteryConsumer.Builder(mCustomPowerComponentCount, + builder = new UserBatteryConsumer.Builder(mCustomPowerComponentNames, mCustomTimeComponentCount, mIncludePowerModels, userId); mUserBatteryConsumerBuilders.put(userId, builder); } diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java index 238f4518b7d3..a0a41f4d6630 100644 --- a/core/java/android/os/PowerComponents.java +++ b/core/java/android/os/PowerComponents.java @@ -26,7 +26,7 @@ import android.annotation.NonNull; class PowerComponents { private static final int CUSTOM_POWER_COMPONENT_OFFSET = BatteryConsumer.POWER_COMPONENT_COUNT - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID; - public static final int CUSTOM_TIME_COMPONENT_OFFSET = BatteryConsumer.TIME_COMPONENT_COUNT + private static final int CUSTOM_TIME_COMPONENT_OFFSET = BatteryConsumer.TIME_COMPONENT_COUNT - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID; private final double mTotalConsumedPowerMah; @@ -34,9 +34,12 @@ class PowerComponents { private final long[] mTimeComponentsMs; private final int mCustomPowerComponentCount; private final byte[] mPowerModels; + // Not written to Parcel and must be explicitly restored during the parent object's unparceling + private String[] mCustomPowerComponentNames; PowerComponents(@NonNull Builder builder) { - mCustomPowerComponentCount = builder.mCustomPowerComponentCount; + mCustomPowerComponentNames = builder.mCustomPowerComponentNames; + mCustomPowerComponentCount = mCustomPowerComponentNames.length; mPowerComponentsMah = builder.mPowerComponentsMah; mTimeComponentsMs = builder.mTimeComponentsMs; mTotalConsumedPowerMah = builder.getTotalPower(); @@ -117,6 +120,26 @@ class PowerComponents { } } + void setCustomPowerComponentNames(String[] customPowerComponentNames) { + mCustomPowerComponentNames = customPowerComponentNames; + } + + public String getCustomPowerComponentName(int componentId) { + if (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + && componentId < BatteryConsumer.LAST_CUSTOM_POWER_COMPONENT_ID) { + try { + return mCustomPowerComponentNames[componentId + - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IllegalArgumentException( + "Unsupported custom power component ID: " + componentId); + } + } else { + throw new IllegalArgumentException( + "Unsupported custom power component ID: " + componentId); + } + } + @BatteryConsumer.PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int component) { if (mPowerModels == null) { @@ -164,20 +187,37 @@ class PowerComponents { } } + public int getCustomPowerComponentCount() { + return mCustomPowerComponentCount; + } + + /** + * Returns the largest usage duration among all time components. + */ + public long getMaxComponentUsageDurationMillis() { + long max = 0; + for (int i = mTimeComponentsMs.length - 1; i >= 0; i--) { + if (mTimeComponentsMs[i] > max) { + max = mTimeComponentsMs[i]; + } + } + return max; + } + /** * Builder for PowerComponents. */ static final class Builder { private final double[] mPowerComponentsMah; - private final int mCustomPowerComponentCount; + private final String[] mCustomPowerComponentNames; private final long[] mTimeComponentsMs; private final byte[] mPowerModels; - Builder(int customPowerComponentCount, int customTimeComponentCount, + Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, boolean includePowerModels) { - mCustomPowerComponentCount = customPowerComponentCount; + mCustomPowerComponentNames = customPowerComponentNames; int powerComponentCount = - BatteryConsumer.POWER_COMPONENT_COUNT + customPowerComponentCount; + BatteryConsumer.POWER_COMPONENT_COUNT + mCustomPowerComponentNames.length; mPowerComponentsMah = new double[powerComponentCount]; mTimeComponentsMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT + customTimeComponentCount]; @@ -285,10 +325,10 @@ class PowerComponents { } public void addPowerAndDuration(Builder other) { - for (int i = 0; i < mPowerComponentsMah.length; i++) { + for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) { mPowerComponentsMah[i] += other.mPowerComponentsMah[i]; } - for (int i = 0; i < mTimeComponentsMs.length; i++) { + for (int i = mTimeComponentsMs.length - 1; i >= 0; i--) { mTimeComponentsMs[i] += other.mTimeComponentsMs[i]; } } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index a42448c261d4..b474d7c95996 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -169,8 +169,9 @@ public class RecoverySystem { public @interface ResumeOnRebootRebootErrorCode {} /** - * The preparation of resume on reboot succeeds. Don't expose it because a successful reboot - * should just reboot the device. + * The preparation of resume on reboot succeeds. + * + * <p> Don't expose it because a successful reboot should just reboot the device. * @hide */ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0; diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java index e973e4c1044e..1327978ad0ba 100644 --- a/core/java/android/os/SystemBatteryConsumer.java +++ b/core/java/android/os/SystemBatteryConsumer.java @@ -104,6 +104,13 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable } /** + * Returns the amount of time this consumer was operating. + */ + public long getUsageDurationMillis() { + return mPowerComponents.getMaxComponentUsageDurationMillis(); + } + + /** * Writes the contents into a Parcel. */ @Override @@ -140,9 +147,9 @@ public class SystemBatteryConsumer extends BatteryConsumer implements Parcelable private double mPowerConsumedByAppsMah; private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; - Builder(int customPowerComponentCount, int customTimeComponentCount, + Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, boolean includePowerModels, @DrainType int drainType) { - super(customPowerComponentCount, customTimeComponentCount, includePowerModels); + super(customPowerComponentNames, customTimeComponentCount, includePowerModels); mDrainType = drainType; } diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index 87c263b3dd38..92e960344f3e 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -139,9 +139,9 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; - public Builder(int customPowerComponentCount, int customTimeComponentCount, + public Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, boolean includePowerModels, @NonNull BatteryStats.Uid batteryStatsUid) { - super(customPowerComponentCount, customTimeComponentCount, includePowerModels); + super(customPowerComponentNames, customTimeComponentCount, includePowerModels); mBatteryStatsUid = batteryStatsUid; mUid = batteryStatsUid.getUid(); } diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java index 78322088a3a4..de0a707fb656 100644 --- a/core/java/android/os/UserBatteryConsumer.java +++ b/core/java/android/os/UserBatteryConsumer.java @@ -77,9 +77,9 @@ public class UserBatteryConsumer extends BatteryConsumer implements Parcelable { private final int mUserId; private List<UidBatteryConsumer.Builder> mUidBatteryConsumers; - Builder(int customPowerComponentCount, int customTimeComponentCount, + Builder(@NonNull String[] customPowerComponentNames, int customTimeComponentCount, boolean includePowerModels, int userId) { - super(customPowerComponentCount, customTimeComponentCount, includePowerModels); + super(customPowerComponentNames, customTimeComponentCount, includePowerModels); mUserId = userId; } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 9bfd75ef2170..51f19ebee987 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -22,10 +22,10 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.UserHandleAware; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentProvider; import android.content.ContentResolver; @@ -129,7 +129,7 @@ public class CallLog { public static final int ERROR_STORAGE_FULL = 2; /** - * Indicates that the {@link InputStream} passed to {@link #storeCallComposerPictureAsUser} + * Indicates that the {@link InputStream} passed to {@link #storeCallComposerPicture} * was closed. * * The caller should retry if this error is encountered, and be sure to not close the stream @@ -195,9 +195,8 @@ public class CallLog { * The caller is responsible for closing the {@link InputStream} after the callback indicating * success or failure. * - * @param context An instance of {@link Context}. - * @param user The user for whom the picture is stored. If {@code null}, the picture will be - * stored for all users. + * @param context An instance of {@link Context}. The picture will be stored to the user + * corresponding to {@link Context#getUser()}. * @param input An input stream from which the picture to store should be read. The input data * must be decodeable as either a JPEG, PNG, or GIF image. * @param executor The {@link Executor} on which to perform the file transfer operation and @@ -207,12 +206,12 @@ public class CallLog { * @hide */ @SystemApi + @UserHandleAware @RequiresPermission(allOf = { Manifest.permission.WRITE_CALL_LOG, Manifest.permission.INTERACT_ACROSS_USERS }) - public static void storeCallComposerPictureAsUser(@NonNull Context context, - @Nullable UserHandle user, + public static void storeCallComposerPicture(@NonNull Context context, @NonNull InputStream input, @CallbackExecutor @NonNull Executor executor, @NonNull OutcomeReceiver<Uri, CallComposerLoggingException> callback) { @@ -246,12 +245,13 @@ public class CallLog { byte[] picData = tmpOut.toByteArray(); UserManager userManager = context.getSystemService(UserManager.class); + UserHandle user = context.getUser(); // Nasty casework for the shadow calllog begins... // First see if we're just inserting for one user. If so, insert into the shadow // based on whether that user is unlocked. UserHandle realUser = UserHandle.CURRENT.equals(user) ? android.os.Process.myUserHandle() : user; - if (realUser != null) { + if (realUser != UserHandle.ALL) { Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI : SHADOW_CALL_COMPOSER_PICTURE_URI; Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri, @@ -625,7 +625,7 @@ public class CallLog { } /** - * @param pictureUri {@link Uri} returned from {@link #storeCallComposerPictureAsUser}. + * @param pictureUri {@link Uri} returned from {@link #storeCallComposerPicture}. * Associates that stored picture with this call in the log. */ public @NonNull AddCallParametersBuilder setPictureUri(@NonNull Uri pictureUri) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 9450994af89f..2616a6676db1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9136,6 +9136,20 @@ public final class Settings { "biometric_debug_enabled"; /** + * Whether or not biometric is allowed on Keyguard. + * @hide + */ + @Readable + public static final String BIOMETRIC_KEYGUARD_ENABLED = "biometric_keyguard_enabled"; + + /** + * Whether or not biometric is allowed for apps (through BiometricPrompt). + * @hide + */ + @Readable + public static final String BIOMETRIC_APP_ENABLED = "biometric_app_enabled"; + + /** * Whether the assist gesture should be enabled. * * @hide diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index bed77e664337..95024b38575b 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -24,9 +24,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.content.AttributionSource; import android.content.Context; -import android.content.ContextParams; import android.os.CancellationSignal; import android.os.IBinder; import android.os.PersistableBundle; @@ -49,7 +47,7 @@ import java.util.concurrent.Executor; @SystemApi @SystemService(Context.UWB_SERVICE) public final class UwbManager { - private static final String SERVICE_NAME = "uwb"; + private static final String SERVICE_NAME = Context.UWB_SERVICE; private final Context mContext; private final IUwbAdapter mUwbAdapter; diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index e4500611db9c..21f75d419a5e 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -104,6 +104,7 @@ public final class SurfaceControl implements Parcelable { private static native void nativeApplyTransaction(long transactionObj, boolean sync); private static native void nativeMergeTransaction(long transactionObj, long otherTransactionObj); + private static native void nativeClearTransaction(long transactionObj); private static native void nativeSetAnimationTransaction(long transactionObj); private static native void nativeSetEarlyWakeupStart(long transactionObj); private static native void nativeSetEarlyWakeupEnd(long transactionObj); @@ -2602,6 +2603,19 @@ public final class SurfaceControl implements Parcelable { } /** + * Clear the transaction object, without applying it. + * + * @hide + */ + public void clear() { + mResizedSurfaces.clear(); + mReparentedSurfaces.clear(); + if (mNativeObject != 0) { + nativeClearTransaction(mNativeObject); + } + } + + /** * Release the native transaction object, without applying it. */ @Override @@ -3411,10 +3425,14 @@ public final class SurfaceControl implements Parcelable { public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { if (mNativeObject == 0) { dest.writeInt(0); - } else { - dest.writeInt(1); + return; } + + dest.writeInt(1); nativeWriteTransactionToParcel(mNativeObject, dest); + if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) { + nativeClearTransaction(mNativeObject); + } } private void readFromParcel(Parcel in) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8c9c6bd8feeb..76eb882b6f53 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1200,7 +1200,8 @@ public final class ViewRootImpl implements ViewParent, Looper.myLooper()); if (mAttachInfo.mThreadedRenderer != null) { - InputMetricsListener listener = new InputMetricsListener(); + InputMetricsListener listener = + new InputMetricsListener(mInputEventReceiver); mHardwareRendererObserver = new HardwareRendererObserver( listener, listener.data, mHandler, true /*waitForPresentTime*/); mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); @@ -1396,9 +1397,6 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; - if (mHardwareRendererObserver != null) { - mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver); - } } } } @@ -3908,7 +3906,10 @@ public final class ViewRootImpl implements ViewParent, mDrawsNeededToReport = 0; mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction); } catch (RemoteException e) { - // Have fun! + Log.e(mTag, "Unable to report draw finished", e); + mSurfaceChangedTransaction.apply(); + } finally { + mSurfaceChangedTransaction.clear(); } } @@ -8083,9 +8084,6 @@ public final class ViewRootImpl implements ViewParent, ThreadedRenderer hardwareRenderer = mAttachInfo.mThreadedRenderer; if (hardwareRenderer != null) { - if (mHardwareRendererObserver != null) { - hardwareRenderer.removeObserver(mHardwareRendererObserver); - } if (mView != null) { hardwareRenderer.destroyHardwareResources(mView); } @@ -8587,12 +8585,18 @@ public final class ViewRootImpl implements ViewParent, super.dispose(); } } - private WindowInputEventReceiver mInputEventReceiver; + WindowInputEventReceiver mInputEventReceiver; final class InputMetricsListener implements HardwareRendererObserver.OnFrameMetricsAvailableListener { public long[] data = new long[FrameMetrics.Index.FRAME_STATS_COUNT]; + private InputEventReceiver mReceiver; + + InputMetricsListener(InputEventReceiver receiver) { + mReceiver = receiver; + } + @Override public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { final int inputEventId = (int) data[FrameMetrics.Index.INPUT_EVENT_ID]; @@ -8605,11 +8609,6 @@ public final class ViewRootImpl implements ViewParent, // available, we cannot compute end-to-end input latency metrics. return; } - final long gpuCompletedTime = data[FrameMetrics.Index.GPU_COMPLETED]; - if (mInputEventReceiver == null) { - return; - } - mInputEventReceiver.reportTimeline(inputEventId, gpuCompletedTime, presentTime); } } HardwareRendererObserver mHardwareRendererObserver; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 6edd07178ed3..616910ab09ca 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2230,9 +2230,7 @@ public final class InputMethodManager { public void removeImeSurface(IBinder windowToken) { synchronized (mH) { try { - final Completable.Void value = Completable.createVoid(); - mService.removeImeSurfaceFromWindow(windowToken, ResultCallbacks.of(value)); - Completable.getResult(value); + mService.removeImeSurfaceFromWindowAsync(windowToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index db8801150f0d..90667cf54f93 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -86,7 +86,8 @@ public abstract class TextClassifierEvent implements Parcelable { TYPE_ACTIONS_SHOWN, TYPE_LINK_CLICKED, TYPE_OVERTYPE, TYPE_COPY_ACTION, TYPE_PASTE_ACTION, TYPE_CUT_ACTION, TYPE_SHARE_ACTION, TYPE_SMART_ACTION, TYPE_SELECTION_DRAG, TYPE_SELECTION_DESTROYED, TYPE_OTHER_ACTION, TYPE_SELECT_ALL, - TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED}) + TYPE_SELECTION_RESET, TYPE_MANUAL_REPLY, TYPE_ACTIONS_GENERATED, TYPE_LINKS_GENERATED, + TYPE_READ_CLIPBOARD}) public @interface Type { // For custom event types, use range 1,000,000+. } @@ -135,6 +136,13 @@ public abstract class TextClassifierEvent implements Parcelable { public static final int TYPE_ACTIONS_GENERATED = 20; /** Some text links were generated.*/ public static final int TYPE_LINKS_GENERATED = 21; + /** + * Read a clipboard. + * TODO: Make this public. + * + * @hide + */ + public static final int TYPE_READ_CLIPBOARD = 22; @Category private final int mEventCategory; diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index f4fdf35fb50f..c09e8bde7417 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -302,6 +302,11 @@ public class UiTranslationController { } final LongSparseArray<ViewTranslationResponse> virtualChildResponse = translatedResult.valueAt(i); + if (DEBUG) { + // TODO(b/182433547): remove before S release + Log.v(TAG, "onVirtualViewTranslationCompleted: receive " + + virtualChildResponse + " for AutofillId " + autofillId); + } mActivity.runOnUiThread(() -> { if (view.getViewTranslationCallback() == null) { if (DEBUG) { @@ -341,8 +346,13 @@ public class UiTranslationController { } for (int i = 0; i < resultCount; i++) { final ViewTranslationResponse response = translatedResult.valueAt(i); + if (DEBUG) { + // TODO(b/182433547): remove before S release + Log.v(TAG, "onTranslationCompleted: response= " + response); + } final AutofillId autofillId = response.getAutofillId(); if (autofillId == null) { + Log.w(TAG, "No AutofillId is set in ViewTranslationResponse:" + response); continue; } final View view = mViews.get(autofillId).get(); @@ -394,6 +404,9 @@ public class UiTranslationController { final TranslationRequest request = new TranslationRequest.Builder() .setViewTranslationRequests(requests) .build(); + if (DEBUG) { + Log.d(TAG, "sendTranslationRequest: request= " + request); + } translator.requestUiTranslate(request, (r) -> r.run(), this::onTranslationCompleted); } @@ -508,10 +521,17 @@ public class UiTranslationController { private void runForEachView(BiConsumer<View, ViewTranslationCallback> action) { synchronized (mLock) { final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews); + if (views.size() == 0) { + Log.w(TAG, "No views can be excuted for runForEachView."); + } mActivity.runOnUiThread(() -> { final int viewCounts = views.size(); for (int i = 0; i < viewCounts; i++) { final View view = views.valueAt(i).get(); + if (DEBUG) { + // TODO(b/182433547): remove before S release + Log.d(TAG, "runForEachView: view= " + view); + } if (view == null || view.getViewTranslationCallback() == null) { if (DEBUG) { Log.d(TAG, "View was gone or ViewTranslationCallback for autofillid " diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index cd91d0266c75..95a3dc788803 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -104,25 +104,31 @@ public class EdgeEffect { /** * The velocity threshold before the spring animation is considered settled. - * The idea here is that velocity should be less than 1 pixel per frame (~16ms). + * The idea here is that velocity should be less than 0.1 pixel per second. */ - private static final double VELOCITY_THRESHOLD = 1.0 / 0.016; + private static final double VELOCITY_THRESHOLD = 0.1; /** * The value threshold before the spring animation is considered close enough to - * the destination to be settled. This should be around 1 pixel. + * the destination to be settled. This should be around 0.01 pixel. */ - private static final double VALUE_THRESHOLD = 1; + private static final double VALUE_THRESHOLD = 0.01; /** * The natural frequency of the stretch spring. */ - private static final double NATURAL_FREQUENCY = 17.55; + private static final double NATURAL_FREQUENCY = 24.657; /** * The damping ratio of the stretch spring. */ - private static final double DAMPING_RATIO = 0.92; + private static final double DAMPING_RATIO = 0.98; + + /** + * The variation of the velocity for the stretch effect when it meets the bound. + * if value is > 1, it will accentuate the absorption of the movement. + */ + private static final float ON_ABSORB_VELOCITY_ADJUSTMENT = 13f; /** @hide */ @IntDef({TYPE_GLOW, TYPE_STRETCH}) @@ -460,7 +466,7 @@ public class EdgeEffect { public void onAbsorb(int velocity) { if (mEdgeEffectType == TYPE_STRETCH) { mState = STATE_RECEDE; - mVelocity = velocity; + mVelocity = velocity * ON_ABSORB_VELOCITY_ADJUSTMENT; mDistance = 0; mStartTime = AnimationUtils.currentAnimationTimeMillis(); } else { @@ -782,8 +788,8 @@ public class EdgeEffect { * considered at rest or false if it is still animating. */ private boolean isAtEquilibrium() { - double displacement = mDistance * mHeight * LINEAR_STRETCH_INTENSITY; // in pixels - double velocity = mVelocity * LINEAR_STRETCH_INTENSITY; + double displacement = mDistance * mHeight; // in pixels + double velocity = mVelocity; return Math.abs(velocity) < VELOCITY_THRESHOLD && Math.abs(displacement) < VALUE_THRESHOLD; } diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java index 296d93c88554..a479b8ac4760 100644 --- a/core/java/android/widget/TextViewTranslationCallback.java +++ b/core/java/android/widget/TextViewTranslationCallback.java @@ -17,6 +17,7 @@ package android.widget; import android.annotation.NonNull; +import android.os.Build; import android.text.method.TranslationTransformationMethod; import android.util.Log; import android.view.View; @@ -34,7 +35,10 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { private static final String TAG = "TextViewTranslationCallback"; - private static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG); + // TODO(b/182433547): remove Build.IS_DEBUGGABLE before ship. Enable the logging in debug build + // to help the debug during the development phase + private static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG) + || Build.IS_DEBUGGABLE; private TranslationTransformationMethod mTranslationTransformation; diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java index 88584f4b2571..d84f571931fd 100644 --- a/core/java/android/window/WindowContextController.java +++ b/core/java/android/window/WindowContextController.java @@ -105,6 +105,7 @@ public class WindowContextController { * a {@link com.android.server.wm.DisplayArea} by * {@link #attachToDisplayArea(int, int, Bundle)}. * + * @see WindowProviderService#attachToWindowToken(IBinder)) * @see IWindowManager#attachWindowContextToWindowToken(IBinder, IBinder) */ public void attachToWindowToken(IBinder windowToken) { diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java new file mode 100644 index 000000000000..b8619fbcf334 --- /dev/null +++ b/core/java/android/window/WindowProviderService.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2021 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.window; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.annotation.CallSuper; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.TestApi; +import android.annotation.UiContext; +import android.app.ActivityThread; +import android.app.LoadedApk; +import android.app.Service; +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.Bundle; +import android.os.IBinder; +import android.view.Display; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams.WindowType; +import android.view.WindowManagerImpl; + +// TODO(b/159767464): handle #onConfigurationChanged(Configuration) +/** + * A {@link Service} responsible for showing a non-activity window, such as software keyboards or + * accessibility overlay windows. This {@link Service} has similar behavior to + * {@link WindowContext}, but is represented as {@link Service}. + * + * @see android.inputmethodservice.InputMethodService + * @see android.accessibilityservice.AccessibilityService + * + * @hide + */ +@TestApi +@UiContext +public abstract class WindowProviderService extends Service { + + private final WindowTokenClient mWindowToken = new WindowTokenClient(); + private final WindowContextController mController = new WindowContextController(mWindowToken); + private WindowManager mWindowManager; + + /** + * Returns the type of this {@link WindowProviderService}. + * Each inheriting class must implement this method to provide the type of the window. It is + * used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)} + * + * @see Context#createWindowContext(int, Bundle) + * + * @hide + */ + @TestApi + @SuppressLint("OnNameExpected") + // Suppress the lint because it is not a callback and users should provide window type + // so we cannot make it final. + public abstract @WindowType int getWindowType(); + + /** + * Returns the option of this {@link WindowProviderService}. + * Default is {@code null}. The inheriting class can implement this method to provide the + * customization {@code option} of the window. It is used similar to {@code options} of + * {@link Context#createWindowContext(int, Bundle)} + * + * @see Context#createWindowContext(int, Bundle) + * + * @hide + */ + @TestApi + @SuppressLint({"OnNameExpected", "NullableCollection"}) + // Suppress the lint because it is not a callback and users may override this API to provide + // launch option. Also, the return value of this API is null by default. + @Nullable + public Bundle getWindowContextOptions() { + return null; + } + + /** + * Attaches this WindowProviderService to the {@code windowToken}. + * + * @hide + */ + @TestApi + public final void attachToWindowToken(@NonNull IBinder windowToken) { + mController.attachToWindowToken(windowToken); + } + + /** @hide */ + @Override + public final Context createServiceBaseContext(ActivityThread mainThread, + LoadedApk packageInfo) { + final Context context = super.createServiceBaseContext(mainThread, packageInfo); + // Always associate with the default display at initialization. + final Display defaultDisplay = context.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY); + return context.createTokenContext(mWindowToken, defaultDisplay); + } + + @CallSuper + @Override + public void onCreate() { + super.onCreate(); + mWindowToken.attachContext(this); + mController.attachToDisplayArea(getWindowType(), getDisplayId(), getWindowContextOptions()); + mWindowManager = WindowManagerImpl.createWindowContextWindowManager(this); + } + + @SuppressLint("OnNameExpected") + @Override + // Suppress the lint because ths is overridden from Context. + public @Nullable Object getSystemService(@NonNull String name) { + if (WINDOW_SERVICE.equals(name)) { + return mWindowManager; + } + return super.getSystemService(name); + } + + @CallSuper + @Override + public void onDestroy() { + super.onDestroy(); + mController.detachIfNeeded(); + } +} diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java index 86f29a8f07ef..84354d90b5f3 100644 --- a/core/java/com/android/internal/app/SuspendedAppActivity.java +++ b/core/java/com/android/internal/app/SuspendedAppActivity.java @@ -27,8 +27,12 @@ import android.annotation.Nullable; import android.app.AlertDialog; import android.app.AppGlobals; import android.app.KeyguardManager; +import android.app.usage.UsageStatsManager; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -64,9 +68,26 @@ public class SuspendedAppActivity extends AlertActivity private int mNeutralButtonAction; private int mUserId; private PackageManager mPm; + private UsageStatsManager mUsm; private Resources mSuspendingAppResources; private SuspendDialogInfo mSuppliedDialogInfo; private Bundle mOptions; + private BroadcastReceiver mUnsuspendReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_PACKAGES_UNSUSPENDED.equals(intent.getAction())) { + final String[] unsuspended = intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_PACKAGE_LIST); + if (ArrayUtils.contains(unsuspended, mSuspendedPackage)) { + if (!isFinishing()) { + Slog.w(TAG, "Package " + mSuspendedPackage + + " got unsuspended while the dialog was visible. Finishing."); + SuspendedAppActivity.this.finish(); + } + } + } + } + }; private CharSequence getAppLabel(String packageName) { try { @@ -183,6 +204,7 @@ public class SuspendedAppActivity extends AlertActivity public void onCreate(Bundle icicle) { super.onCreate(icicle); mPm = getPackageManager(); + mUsm = getSystemService(UsageStatsManager.class); getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); final Intent intent = getIntent(); @@ -222,6 +244,16 @@ public class SuspendedAppActivity extends AlertActivity requestDismissKeyguardIfNeeded(ap.mMessage); setupAlert(); + + final IntentFilter unsuspendFilter = new IntentFilter(Intent.ACTION_PACKAGES_UNSUSPENDED); + registerReceiverAsUser(mUnsuspendReceiver, UserHandle.of(mUserId), unsuspendFilter, null, + null); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(mUnsuspendReceiver); } private void requestDismissKeyguardIfNeeded(CharSequence dismissMessage) { @@ -292,6 +324,7 @@ public class SuspendedAppActivity extends AlertActivity } break; } + mUsm.reportUserInteraction(mSuspendingPackage, mUserId); finish(); } diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java index 2f4958223270..aa416c568dbc 100644 --- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java +++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java @@ -52,8 +52,8 @@ public class AmbientDisplayPowerCalculator extends PowerCalculator { measuredEnergyUC, mPowerEstimator, durationMs); builder.getOrCreateSystemBatteryConsumerBuilder( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah, powerModel) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, powerMah, powerModel) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, durationMs); } /** diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index ac72d290b8b2..cb1900f300c0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -40,6 +40,7 @@ import android.net.INetworkStatsService; import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiManager; +import android.os.BatteryConsumer; import android.os.BatteryManager; import android.os.BatteryStats; import android.os.Binder; @@ -160,7 +161,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 196; + static final int VERSION = 197; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -6947,6 +6948,22 @@ public class BatteryStatsImpl extends BatteryStats { return mGlobalMeasuredEnergyStats.getAccumulatedCustomBucketCharges(); } + /** + * Returns the names of custom power components. + */ + public @NonNull String[] getCustomPowerComponentNames() { + if (mGlobalMeasuredEnergyStats == null) { + return new String[0]; + } + final String[] names = mGlobalMeasuredEnergyStats.getCustomBucketNames(); + for (int i = 0; i < names.length; i++) { + if (TextUtils.isEmpty(names[i])) { + names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i; + } + } + return names; + } + @Override public long getStartClockTime() { final long currentTimeMs = System.currentTimeMillis(); if ((currentTimeMs > MILLISECONDS_IN_YEAR @@ -14379,17 +14396,18 @@ public class BatteryStatsImpl extends BatteryStats { mConstants.startObserving(context.getContentResolver()); registerUsbStateReceiver(context); } + /** * Initialize the measured charge stats data structures. * * @param supportedStandardBuckets boolean array indicating which {@link StandardPowerBucket}s - * are currently supported. - * If null, none are supported (regardless of numCustomBuckets). - * @param numCustomBuckets number of custom (OTHER) EnergyConsumers on this device + * are currently supported. If null, none are supported + * (regardless of customBucketNames). + * @param customBucketNames names of custom (OTHER) EnergyConsumers on this device */ @GuardedBy("this") public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets, - int numCustomBuckets) { + String[] customBucketNames) { boolean supportedBucketMismatch = false; mScreenStateAtLastEnergyMeasurement = mScreenState; @@ -14401,10 +14419,10 @@ public class BatteryStatsImpl extends BatteryStats { } else { if (mGlobalMeasuredEnergyStats == null) { mGlobalMeasuredEnergyStats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); } else { supportedBucketMismatch = !mGlobalMeasuredEnergyStats.isSupportEqualTo( - supportedStandardBuckets, numCustomBuckets); + supportedStandardBuckets, customBucketNames); } if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) { @@ -14423,7 +14441,7 @@ public class BatteryStatsImpl extends BatteryStats { if (supportedBucketMismatch) { mGlobalMeasuredEnergyStats = supportedStandardBuckets == null - ? null : new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + ? null : new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); // Supported power buckets changed since last boot. // Existing data is no longer reliable. resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime()); diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index 49c564b8a737..c3986c27e4ba 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -123,11 +123,7 @@ public class BatteryUsageStatsProvider { final long realtimeUs = mStats.mClocks.elapsedRealtime() * 1000; final long uptimeUs = mStats.mClocks.uptimeMillis() * 1000; - final long[] customMeasuredChargesUC = - mStats.getCustomConsumerMeasuredBatteryConsumptionUC(); - final int customPowerComponentCount = customMeasuredChargesUC != null - ? customMeasuredChargesUC.length - : 0; + final String[] customPowerComponentNames = mStats.getCustomPowerComponentNames(); // TODO(b/174186358): read extra time component number from configuration final int customTimeComponentCount = 0; @@ -136,7 +132,7 @@ public class BatteryUsageStatsProvider { & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; final BatteryUsageStats.Builder batteryUsageStatsBuilder = new BatteryUsageStats.Builder( - customPowerComponentCount, customTimeComponentCount, includePowerModels); + customPowerComponentNames, customTimeComponentCount, includePowerModels); batteryUsageStatsBuilder.setStatsStartTimestamp(mStats.getStartClockTime()); SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java index 59cc66d79bce..4ca59be4877a 100644 --- a/core/java/com/android/internal/os/BinderLatencyObserver.java +++ b/core/java/com/android/internal/os/BinderLatencyObserver.java @@ -16,22 +16,37 @@ package com.android.internal.os; +import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER; + import android.annotation.Nullable; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BinderInternal.CallSession; +import com.android.internal.os.BinderLatencyProto.ApiStats; +import com.android.internal.os.BinderLatencyProto.Dims; +import com.android.internal.os.BinderLatencyProto.RepeatedApiStats; +import com.android.internal.util.FrameworkStatsLog; import java.util.Random; /** Collects statistics about Binder call latency per calling API and method. */ public class BinderLatencyObserver { private static final String TAG = "BinderLatencyObserver"; + private static final int MAX_ATOM_SIZE_BYTES = 4064; + // Be conservative and leave 1K space for the last histogram so we don't go over the size limit. + private static final int LAST_HISTOGRAM_BUFFER_SIZE_BYTES = 1000; + + // Latency observer parameters. public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10; + public static final int STATSD_PUSH_INTERVAL_MINUTES_DEFAULT = 360; // Histogram buckets parameters. public static final int BUCKET_COUNT_DEFAULT = 100; @@ -50,20 +65,124 @@ public class BinderLatencyObserver { private int mFirstBucketSize = FIRST_BUCKET_SIZE_DEFAULT; private float mBucketScaleFactor = BUCKET_SCALE_FACTOR_DEFAULT; + private int mStatsdPushIntervalMinutes = STATSD_PUSH_INTERVAL_MINUTES_DEFAULT; + private final Random mRandom; private BinderLatencyBuckets mLatencyBuckets; + private final Handler mLatencyObserverHandler; + + private Runnable mLatencyObserverRunnable = new Runnable() { + @Override + public void run() { + // Schedule the next push. + noteLatencyDelayed(); + + ArrayMap<LatencyDims, int[]> histogramMap; + synchronized (mLock) { + // Copy the histograms map so we don't use the lock for longer than needed. + histogramMap = new ArrayMap<>(mLatencyHistograms); + mLatencyHistograms.clear(); + } + + BinderTransactionNameResolver resolver = new BinderTransactionNameResolver(); + ProtoOutputStream proto = new ProtoOutputStream(); + int histogramsWritten = 0; + + for (LatencyDims dims : histogramMap.keySet()) { + // Start a new atom if the next histogram risks going over the atom size limit. + if (proto.getRawSize() + LAST_HISTOGRAM_BUFFER_SIZE_BYTES > getMaxAtomSizeBytes()) { + if (histogramsWritten > 0) { + writeAtomToStatsd(proto); + } + proto = new ProtoOutputStream(); + histogramsWritten = 0; + } + + String transactionName = resolver.getMethodName( + dims.getBinderClass(), dims.getTransactionCode()); + fillApiStatsProto(proto, dims, transactionName, histogramMap.get(dims)); + histogramsWritten++; + } + // Push the final atom. + if (histogramsWritten > 0) { + writeAtomToStatsd(proto); + } + } + }; + + private void fillApiStatsProto( + ProtoOutputStream proto, LatencyDims dims, String transactionName, int[] histogram) { + // Find the part of the histogram to write. + int firstNonEmptyBucket = 0; + for (int i = 0; i < mBucketCount; i++) { + if (histogram[i] != 0) { + firstNonEmptyBucket = i; + break; + } + } + int lastNonEmptyBucket = mBucketCount - 1; + for (int i = mBucketCount - 1; i >= 0; i--) { + if (histogram[i] != 0) { + lastNonEmptyBucket = i; + break; + } + } + + // Start a new ApiStats proto. + long apiStatsToken = proto.start(RepeatedApiStats.API_STATS); + + // Write the dims. + long dimsToken = proto.start(ApiStats.DIMS); + proto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + proto.write(Dims.SERVICE_CLASS_NAME, dims.getBinderClass().getName()); + proto.write(Dims.SERVICE_METHOD_NAME, transactionName); + proto.end(dimsToken); + + // Write the histogram. + proto.write(ApiStats.FIRST_BUCKET_INDEX, firstNonEmptyBucket); + for (int i = firstNonEmptyBucket; i <= lastNonEmptyBucket; i++) { + proto.write(ApiStats.BUCKETS, histogram[i]); + } + + proto.end(apiStatsToken); + } + + protected int getMaxAtomSizeBytes() { + return MAX_ATOM_SIZE_BYTES; + } + + protected void writeAtomToStatsd(ProtoOutputStream atom) { + FrameworkStatsLog.write( + FrameworkStatsLog.BINDER_LATENCY_REPORTED, + atom.getBytes(), + mPeriodicSamplingInterval, + 1); + } + + private void noteLatencyDelayed() { + mLatencyObserverHandler.removeCallbacks(mLatencyObserverRunnable); + mLatencyObserverHandler.postDelayed(mLatencyObserverRunnable, + mStatsdPushIntervalMinutes * 60 * 1000); + } + /** Injector for {@link BinderLatencyObserver}. */ public static class Injector { public Random getRandomGenerator() { return new Random(); } + + public Handler getHandler() { + return new Handler(Looper.getMainLooper()); + } } public BinderLatencyObserver(Injector injector) { mRandom = injector.getRandomGenerator(); + mLatencyObserverHandler = injector.getHandler(); mLatencyBuckets = new BinderLatencyBuckets( mBucketCount, mFirstBucketSize, mBucketScaleFactor); + noteLatencyDelayed(); } /** Should be called when a Binder call completes, will store latency data. */ @@ -73,7 +192,8 @@ public class BinderLatencyObserver { } LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode); - long callDuration = getElapsedRealtimeMicro() - s.timeStarted; + long elapsedTimeMicro = getElapsedRealtimeMicro(); + long callDuration = elapsedTimeMicro - s.timeStarted; // Find the bucket this sample should go to. int bucketIdx = mLatencyBuckets.sampleToBucket( @@ -117,6 +237,22 @@ public class BinderLatencyObserver { } } + /** Updates the statsd push interval. */ + public void setPushInterval(int pushIntervalMinutes) { + if (pushIntervalMinutes <= 0) { + Slog.w(TAG, "Ignored invalid push interval (value must be positive): " + + pushIntervalMinutes); + return; + } + + synchronized (mLock) { + if (pushIntervalMinutes != mStatsdPushIntervalMinutes) { + mStatsdPushIntervalMinutes = pushIntervalMinutes; + reset(); + } + } + } + /** Updates the histogram buckets parameters. */ public void setHistogramBucketsParams( int bucketCount, int firstBucketSize, float bucketScaleFactor) { @@ -138,6 +274,7 @@ public class BinderLatencyObserver { synchronized (mLock) { mLatencyHistograms.clear(); } + noteLatencyDelayed(); } /** Container for binder latency information. */ @@ -187,4 +324,9 @@ public class BinderLatencyObserver { public ArrayMap<LatencyDims, int[]> getLatencyHistograms() { return mLatencyHistograms; } + + @VisibleForTesting + public Runnable getStatsdPushRunnable() { + return mLatencyObserverRunnable; + } } diff --git a/core/java/com/android/internal/os/IdlePowerCalculator.java b/core/java/com/android/internal/os/IdlePowerCalculator.java index 4a4991bd3966..5cb54bd7fbc9 100644 --- a/core/java/com/android/internal/os/IdlePowerCalculator.java +++ b/core/java/com/android/internal/os/IdlePowerCalculator.java @@ -54,8 +54,8 @@ public class IdlePowerCalculator extends PowerCalculator { BatteryStats.STATS_SINCE_CHARGED); if (mPowerMah != 0) { builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_IDLE) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, mPowerMah) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, mDurationMs); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE, mPowerMah) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_IDLE, mDurationMs); } } diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java index 21dcce9fb64f..9ec40c6f6eed 100644 --- a/core/java/com/android/internal/os/MemoryPowerCalculator.java +++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java @@ -32,8 +32,8 @@ public class MemoryPowerCalculator extends PowerCalculator { final double powerMah = calculatePower(batteryStats, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_MEMORY) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah); + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MEMORY, durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY, powerMah); } @Override diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java index 362ca0761b2c..6f279d99a5c9 100644 --- a/core/java/com/android/internal/os/PhonePowerCalculator.java +++ b/core/java/com/android/internal/os/PhonePowerCalculator.java @@ -45,8 +45,8 @@ public class PhonePowerCalculator extends PowerCalculator { final double phoneOnPower = mPowerEstimator.calculatePower(phoneOnTimeMs); if (phoneOnPower != 0) { builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_PHONE) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, phoneOnPower) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, phoneOnTimeMs); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_PHONE, phoneOnPower) + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_PHONE, phoneOnTimeMs); } } diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 0267def918c2..0743c897a91f 100644 --- a/core/java/com/android/internal/os/ScreenPowerCalculator.java +++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java @@ -96,9 +96,9 @@ public class ScreenPowerCalculator extends PowerCalculator { } builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_SCREEN) - .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN, totalPowerAndDuration.durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, Math.max(totalPowerAndDuration.powerMah, totalAppPower), powerModel) .setPowerConsumedByApps(totalAppPower); } diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java index 00a0627d068c..3153071fd75d 100644 --- a/core/java/com/android/internal/power/MeasuredEnergyStats.java +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -23,6 +23,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; +import android.text.TextUtils; import android.util.DebugUtils; import android.util.Slog; import android.view.Display; @@ -88,13 +89,15 @@ public class MeasuredEnergyStats { */ private final long[] mAccumulatedChargeMicroCoulomb; + private final String[] mCustomBucketNames; + /** * Creates a MeasuredEnergyStats set to support the provided power buckets. * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_POWER_BUCKETS}. * numCustomBuckets >= 0 is the number of (non-standard) custom power buckets on the device. */ - public MeasuredEnergyStats(boolean[] supportedStandardBuckets, int numCustomBuckets) { - final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + numCustomBuckets; + public MeasuredEnergyStats(boolean[] supportedStandardBuckets, String[] customBucketNames) { + final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length; mAccumulatedChargeMicroCoulomb = new long[numTotalBuckets]; // Initialize to all zeros where supported, otherwise POWER_DATA_UNAVAILABLE. // All custom buckets are, by definition, supported, so their values stay at 0. @@ -103,6 +106,7 @@ public class MeasuredEnergyStats { mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE; } } + mCustomBucketNames = customBucketNames; } /** @@ -119,6 +123,7 @@ public class MeasuredEnergyStats { mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE; } } + mCustomBucketNames = template.getCustomBucketNames(); } /** @@ -135,6 +140,7 @@ public class MeasuredEnergyStats { */ private MeasuredEnergyStats(int numIndices) { mAccumulatedChargeMicroCoulomb = new long[numIndices]; + mCustomBucketNames = new String[0]; } /** Construct from parcel. */ @@ -142,12 +148,14 @@ public class MeasuredEnergyStats { final int size = in.readInt(); mAccumulatedChargeMicroCoulomb = new long[size]; in.readLongArray(mAccumulatedChargeMicroCoulomb); + mCustomBucketNames = in.readStringArray(); } /** Write to parcel */ public void writeToParcel(Parcel out) { out.writeInt(mAccumulatedChargeMicroCoulomb.length); out.writeLongArray(mAccumulatedChargeMicroCoulomb); + out.writeStringArray(mCustomBucketNames); } /** @@ -294,7 +302,7 @@ public class MeasuredEnergyStats { final int numCustomBuckets = arraySize - NUMBER_STANDARD_POWER_BUCKETS; final MeasuredEnergyStats stats = new MeasuredEnergyStats( - new boolean[NUMBER_STANDARD_POWER_BUCKETS], numCustomBuckets); + new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[numCustomBuckets]); stats.readSummaryFromParcel(in, true); return stats; } @@ -406,12 +414,12 @@ public class MeasuredEnergyStats { /** Check if the supported power buckets are precisely those given. */ public boolean isSupportEqualTo( - @NonNull boolean[] queriedStandardBuckets, int numCustomBuckets) { + @NonNull boolean[] queriedStandardBuckets, String[] customBucketNames) { final int numBuckets = getNumberOfIndices(); // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just // quantitatively, and treat as mismatch if so. - if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + numCustomBuckets) { + if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length) { return false; } for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) { @@ -422,6 +430,10 @@ public class MeasuredEnergyStats { return true; } + public String[] getCustomBucketNames() { + return mCustomBucketNames; + } + /** Dump debug data. */ public void dump(PrintWriter pw) { pw.print(" "); @@ -443,11 +455,16 @@ public class MeasuredEnergyStats { * If the index is a standard bucket, returns its name; otherwise returns its prefixed custom * bucket number. */ - private static String getBucketName(int index) { + private String getBucketName(int index) { if (isValidStandardBucket(index)) { return DebugUtils.valueToString(MeasuredEnergyStats.class, "POWER_BUCKET_", index); } - return "CUSTOM_" + indexToCustomBucket(index); + final int customBucket = indexToCustomBucket(index); + StringBuilder name = new StringBuilder().append("CUSTOM_").append(customBucket); + if (mCustomBucketNames != null && !TextUtils.isEmpty(mCustomBucketNames[customBucket])) { + name.append('(').append(mCustomBucketNames[customBucket]).append(')'); + } + return name.toString(); } /** Get the number of custom power buckets on this device. */ diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 50bbfc5ccb08..fd13c26b05b2 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -89,8 +89,7 @@ interface IInputMethodManager { /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */ oneway void removeImeSurface(in IVoidResultCallback resultCallback); /** Remove the IME surface. Requires passing the currently focused window. */ - oneway void removeImeSurfaceFromWindow(in IBinder windowToken, - in IVoidResultCallback resultCallback); + oneway void removeImeSurfaceFromWindowAsync(in IBinder windowToken); oneway void startProtoDump(in byte[] protoDump, int source, String where, in IVoidResultCallback resultCallback); oneway void isImeTraceEnabled(in IBooleanResultCallback resultCallback); diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 8ecc80946141..bab4e93bdd9a 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -64,6 +64,7 @@ import com.android.internal.R; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Consumer; @@ -530,13 +531,7 @@ public class ConversationLayout extends FrameLayout mConversationText.setText(conversationText); // Update if the groups can hide the sender if they are first (applies to 1:1 conversations) // This needs to happen after all of the above o update all of the groups - for (int i = mGroups.size() - 1; i >= 0; i--) { - MessagingGroup messagingGroup = mGroups.get(i); - CharSequence messageSender = messagingGroup.getSenderName(); - boolean canHide = mIsOneToOne - && TextUtils.equals(conversationText, messageSender); - messagingGroup.setCanHideSenderIfFirst(canHide); - } + mPeopleHelper.maybeHideFirstSenderName(mGroups, mIsOneToOne, conversationText); updateAppName(); updateIconPositionAndSize(); updateImageMessages(); @@ -779,35 +774,7 @@ public class ConversationLayout extends FrameLayout private void updateTitleAndNamesDisplay() { // Map of unique names to their prefix - ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); - // Map of single-character string prefix to the only name which uses it, or null if multiple - ArrayMap<String, CharSequence> uniqueCharacters = new ArrayMap<>(); - for (int i = 0; i < mGroups.size(); i++) { - MessagingGroup group = mGroups.get(i); - CharSequence senderName = group.getSenderName(); - if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) { - continue; - } - if (!uniqueNames.containsKey(senderName)) { - String charPrefix = mPeopleHelper.findNamePrefix(senderName, null); - if (charPrefix == null) { - continue; - } - if (uniqueCharacters.containsKey(charPrefix)) { - // this character was already used, lets make it more unique. We first need to - // resolve the existing character if it exists - CharSequence existingName = uniqueCharacters.get(charPrefix); - if (existingName != null) { - uniqueNames.put(existingName, mPeopleHelper.findNameSplit(existingName)); - uniqueCharacters.put(charPrefix, null); - } - uniqueNames.put(senderName, mPeopleHelper.findNameSplit(senderName)); - } else { - uniqueNames.put(senderName, charPrefix); - uniqueCharacters.put(charPrefix, senderName); - } - } - } + Map<CharSequence, String> uniqueNames = mPeopleHelper.mapUniqueNamesToPrefix(mGroups); // Now that we have the correct symbols, let's look what we have cached ArrayMap<CharSequence, Icon> cachedAvatars = new ArrayMap<>(); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a0e50be93410..db4e6734c848 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -933,19 +933,6 @@ public class LockPatternUtils { } /** - * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash. - * Not the most secure, but it is at least a second level of protection. First level is that - * the file is in a location only readable by the system process. - * - * @param password the gesture pattern. - * - * @return the hash of the pattern in a byte array. - */ - public String legacyPasswordToHash(byte[] password, int userId) { - return LockscreenCredential.legacyPasswordToHash(password, getSalt(userId).getBytes()); - } - - /** * Returns the credential type of the user, can be one of {@link #CREDENTIAL_TYPE_NONE}, * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and * {@link #CREDENTIAL_TYPE_PASSWORD} diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index f5df3abad658..940979d7cd1f 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -16,15 +16,41 @@ package com.android.internal.widget; +import android.annotation.IntDef; import android.annotation.Nullable; import android.app.admin.PasswordMetrics; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * LockSettingsService local system service interface. * * @hide Only for use within the system server. */ public abstract class LockSettingsInternal { + /** ErrorCode for armRebootEscrow failures. **/ + @IntDef(prefix = {"ARM_REBOOT_ERROR_"}, value = { + ARM_REBOOT_ERROR_NONE, + ARM_REBOOT_ERROR_UNSPECIFIED, + ARM_REBOOT_ERROR_ESCROW_NOT_READY, + ARM_REBOOT_ERROR_NO_PROVIDER, + ARM_REBOOT_ERROR_PROVIDER_MISMATCH, + ARM_REBOOT_ERROR_NO_ESCROW_KEY, + ARM_REBOOT_ERROR_KEYSTORE_FAILURE, + ARM_REBOOT_ERROR_STORE_ESCROW_KEY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ArmRebootEscrowErrorCode {} + + public static final int ARM_REBOOT_ERROR_NONE = 0; + public static final int ARM_REBOOT_ERROR_UNSPECIFIED = 1; + public static final int ARM_REBOOT_ERROR_ESCROW_NOT_READY = 2; + public static final int ARM_REBOOT_ERROR_NO_PROVIDER = 3; + public static final int ARM_REBOOT_ERROR_PROVIDER_MISMATCH = 4; + public static final int ARM_REBOOT_ERROR_NO_ESCROW_KEY = 5; + public static final int ARM_REBOOT_ERROR_KEYSTORE_FAILURE = 6; + public static final int ARM_REBOOT_ERROR_STORE_ESCROW_KEY = 7; + // TODO(b/183140900) split store escrow key errors into detailed ones. /** * Create an escrow token for the current user, which can later be used to unlock FBE @@ -104,9 +130,9 @@ public abstract class LockSettingsInternal { * Should be called immediately before rebooting for an update. This depends on {@link * #prepareRebootEscrow()} having been called and the escrow completing. * - * @return true if the arming worked + * @return ARM_ERROR_NONE if the arming worked */ - public abstract boolean armRebootEscrow(); + public abstract @ArmRebootEscrowErrorCode int armRebootEscrow(); /** diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java index f312d1d4f25d..f30b8442dc35 100644 --- a/core/java/com/android/internal/widget/MessagingGroup.java +++ b/core/java/com/android/internal/widget/MessagingGroup.java @@ -24,6 +24,7 @@ import android.annotation.StyleRes; import android.app.Person; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; @@ -109,7 +110,10 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou private boolean mIsInConversation = true; private ViewGroup mMessagingIconContainer; private int mConversationContentStart; - private int mNonConversationMarginEnd; + private int mNonConversationContentStart; + private int mNonConversationPaddingStart; + private int mConversationAvatarSize; + private int mNonConversationAvatarSize; private int mNotificationTextMarginTop; public MessagingGroup(@NonNull Context context) { @@ -141,16 +145,21 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mMessagingIconContainer = findViewById(R.id.message_icon_container); mContentContainer = findViewById(R.id.messaging_group_content_container); mSendingSpinnerContainer = findViewById(R.id.messaging_group_sending_progress_container); - DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + Resources res = getResources(); + DisplayMetrics displayMetrics = res.getDisplayMetrics(); mDisplaySize.x = displayMetrics.widthPixels; mDisplaySize.y = displayMetrics.heightPixels; - mSenderTextPaddingSingleLine = getResources().getDimensionPixelSize( + mSenderTextPaddingSingleLine = res.getDimensionPixelSize( R.dimen.messaging_group_singleline_sender_padding_end); - mConversationContentStart = getResources().getDimensionPixelSize( - R.dimen.conversation_content_start); - mNonConversationMarginEnd = getResources().getDimensionPixelSize( - R.dimen.messaging_layout_margin_end); - mNotificationTextMarginTop = getResources().getDimensionPixelSize( + mConversationContentStart = res.getDimensionPixelSize(R.dimen.conversation_content_start); + mNonConversationContentStart = res.getDimensionPixelSize( + R.dimen.notification_content_margin_start); + mNonConversationPaddingStart = res.getDimensionPixelSize( + R.dimen.messaging_layout_icon_padding_start); + mConversationAvatarSize = res.getDimensionPixelSize(R.dimen.messaging_avatar_size); + mNonConversationAvatarSize = res.getDimensionPixelSize( + R.dimen.notification_icon_circle_size); + mNotificationTextMarginTop = res.getDimensionPixelSize( R.dimen.notification_text_margin_top); } @@ -696,10 +705,18 @@ public class MessagingGroup extends LinearLayout implements MessagingLinearLayou mIsInConversation = isInConversation; MarginLayoutParams layoutParams = (MarginLayoutParams) mMessagingIconContainer.getLayoutParams(); - layoutParams.width = mIsInConversation ? mConversationContentStart - : ViewPager.LayoutParams.WRAP_CONTENT; - layoutParams.setMarginEnd(mIsInConversation ? 0 : mNonConversationMarginEnd); + layoutParams.width = mIsInConversation + ? mConversationContentStart + : mNonConversationContentStart; mMessagingIconContainer.setLayoutParams(layoutParams); + int imagePaddingStart = isInConversation ? 0 : mNonConversationPaddingStart; + mMessagingIconContainer.setPaddingRelative(imagePaddingStart, 0, 0, 0); + + ViewGroup.LayoutParams avatarLayoutParams = mAvatarView.getLayoutParams(); + int size = mIsInConversation ? mConversationAvatarSize : mNonConversationAvatarSize; + avatarLayoutParams.height = size; + avatarLayoutParams.width = size; + mAvatarView.setLayoutParams(avatarLayoutParams); } } diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java index 27cd6e13d86c..e1602a981920 100644 --- a/core/java/com/android/internal/widget/MessagingLayout.java +++ b/core/java/com/android/internal/widget/MessagingLayout.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_AT_END; +import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL; import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE; import android.annotation.AttrRes; @@ -27,10 +27,6 @@ import android.app.Notification; import android.app.Person; import android.app.RemoteInputHistoryItem; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -40,21 +36,22 @@ import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.RemotableViewMethod; +import android.view.View; +import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.RemoteViews; -import android.widget.TextView; import com.android.internal.R; -import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.Consumer; -import java.util.regex.Pattern; /** * A custom-built layout for the Notification.MessagingStyle allows dynamic addition and removal @@ -65,15 +62,6 @@ public class MessagingLayout extends FrameLayout implements ImageMessageConsumer, IMessagingLayout { private static final float COLOR_SHIFT_AMOUNT = 60; - /** - * Pattren for filter some ingonable characters. - * p{Z} for any kind of whitespace or invisible separator. - * p{C} for any kind of punctuation character. - */ - private static final Pattern IGNORABLE_CHAR_PATTERN - = Pattern.compile("[\\p{C}\\p{Z}]"); - private static final Pattern SPECIAL_CHAR_PATTERN - = Pattern.compile ("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); private static final Consumer<MessagingMessage> REMOVE_MESSAGE = MessagingMessage::removeMessage; public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); @@ -81,26 +69,26 @@ public class MessagingLayout extends FrameLayout public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); public static final OnLayoutChangeListener MESSAGING_PROPERTY_ANIMATOR = new MessagingPropertyAnimator(); + private final PeopleHelper mPeopleHelper = new PeopleHelper(); private List<MessagingMessage> mMessages = new ArrayList<>(); private List<MessagingMessage> mHistoricMessages = new ArrayList<>(); private MessagingLinearLayout mMessagingLinearLayout; private boolean mShowHistoricMessages; private ArrayList<MessagingGroup> mGroups = new ArrayList<>(); - private TextView mTitleView; + private MessagingLinearLayout mImageMessageContainer; + private ImageView mRightIconView; + private Rect mMessagingClipRect; private int mLayoutColor; private int mSenderTextColor; private int mMessageTextColor; - private int mAvatarSize; - private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private Paint mTextPaint = new Paint(); - private CharSequence mConversationTitle; private Icon mAvatarReplacement; private boolean mIsOneToOne; private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); private Person mUser; private CharSequence mNameReplacement; - private boolean mDisplayImagesAtEnd; + private boolean mIsCollapsed; private ImageResolver mImageResolver; + private CharSequence mConversationTitle; public MessagingLayout(@NonNull Context context) { super(context); @@ -123,17 +111,16 @@ public class MessagingLayout extends FrameLayout @Override protected void onFinishInflate() { super.onFinishInflate(); + mPeopleHelper.init(getContext()); mMessagingLinearLayout = findViewById(R.id.notification_messaging); + mImageMessageContainer = findViewById(R.id.conversation_image_message_container); + mRightIconView = findViewById(R.id.right_icon); // We still want to clip, but only on the top, since views can temporarily out of bounds // during transitions. DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); int size = Math.max(displayMetrics.widthPixels, displayMetrics.heightPixels); - Rect rect = new Rect(0, 0, size, size); - mMessagingLinearLayout.setClipBounds(rect); - mTitleView = findViewById(R.id.title); - mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size); - mTextPaint.setTextAlign(Paint.Align.CENTER); - mTextPaint.setAntiAlias(true); + mMessagingClipRect = new Rect(0, 0, size, size); + setMessagingClippingDisabled(false); } @RemotableViewMethod @@ -153,7 +140,7 @@ public class MessagingLayout extends FrameLayout */ @RemotableViewMethod public void setIsCollapsed(boolean isCollapsed) { - mDisplayImagesAtEnd = isCollapsed; + mIsCollapsed = isCollapsed; } @RemotableViewMethod @@ -168,7 +155,7 @@ public class MessagingLayout extends FrameLayout */ @RemotableViewMethod public void setConversationTitle(CharSequence conversationTitle) { - // Unused + mConversationTitle = conversationTitle; } @RemotableViewMethod @@ -180,11 +167,6 @@ public class MessagingLayout extends FrameLayout List<Notification.MessagingStyle.Message> newHistoricMessages = Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages); setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON)); - mConversationTitle = null; - TextView headerText = findViewById(R.id.header_text); - if (headerText != null) { - mConversationTitle = headerText.getText(); - } RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); addRemoteInputHistoryToMessages(newMessages, history); @@ -238,6 +220,41 @@ public class MessagingLayout extends FrameLayout updateHistoricMessageVisibility(); updateTitleAndNamesDisplay(); + // after groups are finalized, hide the first sender name if it's showing as the title + mPeopleHelper.maybeHideFirstSenderName(mGroups, mIsOneToOne, mConversationTitle); + updateImageMessages(); + } + + private void updateImageMessages() { + View newMessage = null; + if (mImageMessageContainer == null) { + return; + } + if (mIsCollapsed && !mGroups.isEmpty()) { + // When collapsed, we're displaying the image message in a dedicated container + // on the right of the layout instead of inline. Let's add the isolated image there + MessagingGroup messagingGroup = mGroups.get(mGroups.size() - 1); + MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage(); + if (isolatedMessage != null) { + newMessage = isolatedMessage.getView(); + } + } + // Remove all messages that don't belong into the image layout + View previousMessage = mImageMessageContainer.getChildAt(0); + if (previousMessage != newMessage) { + mImageMessageContainer.removeView(previousMessage); + if (newMessage != null) { + mImageMessageContainer.addView(newMessage); + } + } + mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE); + + // When showing an image message, do not show the large icon. Removing the drawable + // prevents it from being shown in the left_icon view (by the grouping util). + if (newMessage != null && mRightIconView != null && mRightIconView.getDrawable() != null) { + mRightIconView.setImageDrawable(null); + mRightIconView.setVisibility(GONE); + } } private void removeGroups(ArrayList<MessagingGroup> oldGroups) { @@ -266,34 +283,7 @@ public class MessagingLayout extends FrameLayout } private void updateTitleAndNamesDisplay() { - ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); - ArrayMap<Character, CharSequence> uniqueCharacters = new ArrayMap<>(); - for (int i = 0; i < mGroups.size(); i++) { - MessagingGroup group = mGroups.get(i); - CharSequence senderName = group.getSenderName(); - if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) { - continue; - } - if (!uniqueNames.containsKey(senderName)) { - // Only use visible characters to get uniqueNames - String pureSenderName = IGNORABLE_CHAR_PATTERN - .matcher(senderName).replaceAll("" /* replacement */); - char c = pureSenderName.charAt(0); - if (uniqueCharacters.containsKey(c)) { - // this character was already used, lets make it more unique. We first need to - // resolve the existing character if it exists - CharSequence existingName = uniqueCharacters.get(c); - if (existingName != null) { - uniqueNames.put(existingName, findNameSplit((String) existingName)); - uniqueCharacters.put(c, null); - } - uniqueNames.put(senderName, findNameSplit((String) senderName)); - } else { - uniqueNames.put(senderName, Character.toString(c)); - uniqueCharacters.put(c, pureSenderName); - } - } - } + Map<CharSequence, String> uniqueNames = mPeopleHelper.mapUniqueNamesToPrefix(mGroups); // Now that we have the correct symbols, let's look what we have cached ArrayMap<CharSequence, Icon> cachedAvatars = new ArrayMap<>(); @@ -337,26 +327,7 @@ public class MessagingLayout extends FrameLayout } public Icon createAvatarSymbol(CharSequence senderName, String symbol, int layoutColor) { - if (symbol.isEmpty() || TextUtils.isDigitsOnly(symbol) || - SPECIAL_CHAR_PATTERN.matcher(symbol).find()) { - Icon avatarIcon = Icon.createWithResource(getContext(), - com.android.internal.R.drawable.messaging_user); - avatarIcon.setTint(findColor(senderName, layoutColor)); - return avatarIcon; - } else { - Bitmap bitmap = Bitmap.createBitmap(mAvatarSize, mAvatarSize, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - float radius = mAvatarSize / 2.0f; - int color = findColor(senderName, layoutColor); - mPaint.setColor(color); - canvas.drawCircle(radius, radius, radius, mPaint); - boolean needDarkText = ColorUtils.calculateLuminance(color) > 0.5f; - mTextPaint.setColor(needDarkText ? Color.BLACK : Color.WHITE); - mTextPaint.setTextSize(symbol.length() == 1 ? mAvatarSize * 0.5f : mAvatarSize * 0.3f); - int yPos = (int) (radius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2)); - canvas.drawText(symbol, radius, yPos, mTextPaint); - return Icon.createWithBitmap(bitmap); - } + return mPeopleHelper.createAvatarSymbol(senderName, symbol, layoutColor); } private int findColor(CharSequence senderName, int layoutColor) { @@ -449,8 +420,8 @@ public class MessagingLayout extends FrameLayout newGroup = MessagingGroup.createGroup(mMessagingLinearLayout); mAddedGroups.add(newGroup); } - newGroup.setImageDisplayLocation(mDisplayImagesAtEnd - ? IMAGE_DISPLAY_LOCATION_AT_END + newGroup.setImageDisplayLocation(mIsCollapsed + ? IMAGE_DISPLAY_LOCATION_EXTERNAL : IMAGE_DISPLAY_LOCATION_INLINE); newGroup.setIsInConversation(false); newGroup.setLayoutColor(mLayoutColor); @@ -460,6 +431,8 @@ public class MessagingLayout extends FrameLayout if (sender != mUser && mNameReplacement != null) { nameOverride = mNameReplacement; } + newGroup.setSingleLine(mIsCollapsed); + newGroup.setShowingAvatar(!mIsCollapsed); newGroup.setSender(sender, nameOverride); newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner); mGroups.add(newGroup); @@ -600,12 +573,17 @@ public class MessagingLayout extends FrameLayout return mMessagingLinearLayout; } + @Nullable + public ViewGroup getImageMessageContainer() { + return mImageMessageContainer; + } + public ArrayList<MessagingGroup> getMessagingGroups() { return mGroups; } @Override public void setMessagingClippingDisabled(boolean clippingDisabled) { - // Don't do anything, this is only used for the ConversationLayout + mMessagingLinearLayout.setClipBounds(clippingDisabled ? null : mMessagingClipRect); } } diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java index 7a8ead6b33f1..de3b6a4dacb2 100644 --- a/core/java/com/android/internal/widget/NotificationExpandButton.java +++ b/core/java/com/android/internal/widget/NotificationExpandButton.java @@ -165,15 +165,23 @@ public class NotificationExpandButton extends FrameLayout { private void updateColors() { if (shouldShowNumber() && !mDisallowColor) { - mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor)); + if (mHighlightPillColor != 0) { + mPillView.setBackgroundTintList(ColorStateList.valueOf(mHighlightPillColor)); + } mPillView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN); mIconView.setColorFilter(mHighlightTextColor, PorterDuff.Mode.SRC_IN); - mNumberView.setTextColor(mHighlightTextColor); + if (mHighlightTextColor != 0) { + mNumberView.setTextColor(mHighlightTextColor); + } } else { - mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor)); + if (mDefaultPillColor != 0) { + mPillView.setBackgroundTintList(ColorStateList.valueOf(mDefaultPillColor)); + } mPillView.setBackgroundTintMode(PorterDuff.Mode.SRC_IN); mIconView.setColorFilter(mDefaultTextColor, PorterDuff.Mode.SRC_IN); - mNumberView.setTextColor(mDefaultTextColor); + if (mDefaultTextColor != 0) { + mNumberView.setTextColor(mDefaultTextColor); + } } } diff --git a/core/java/com/android/internal/widget/PeopleHelper.java b/core/java/com/android/internal/widget/PeopleHelper.java index 77f4c8f6bede..85cedc362b99 100644 --- a/core/java/com/android/internal/widget/PeopleHelper.java +++ b/core/java/com/android/internal/widget/PeopleHelper.java @@ -21,6 +21,7 @@ import static com.android.internal.widget.MessagingPropertyAnimator.ALPHA_OUT; import android.annotation.ColorInt; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -28,12 +29,15 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Icon; import android.text.TextUtils; +import android.util.ArrayMap; import android.view.View; import com.android.internal.R; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; /** @@ -176,4 +180,58 @@ public class PeopleHelper { } return findNamePrefix(name, ""); } + + /** + * Creates a mapping of the unique sender names in the groups to the string 1- or 2-character + * prefix strings for the names, which are extracted as the initials, and should be used for + * generating the avatar. Senders not requiring a generated avatar, or with an empty name are + * omitted. + */ + public Map<CharSequence, String> mapUniqueNamesToPrefix(List<MessagingGroup> groups) { + // Map of unique names to their prefix + ArrayMap<CharSequence, String> uniqueNames = new ArrayMap<>(); + // Map of single-character string prefix to the only name which uses it, or null if multiple + ArrayMap<String, CharSequence> uniqueCharacters = new ArrayMap<>(); + for (int i = 0; i < groups.size(); i++) { + MessagingGroup group = groups.get(i); + CharSequence senderName = group.getSenderName(); + if (!group.needsGeneratedAvatar() || TextUtils.isEmpty(senderName)) { + continue; + } + if (!uniqueNames.containsKey(senderName)) { + String charPrefix = findNamePrefix(senderName, null); + if (charPrefix == null) { + continue; + } + if (uniqueCharacters.containsKey(charPrefix)) { + // this character was already used, lets make it more unique. We first need to + // resolve the existing character if it exists + CharSequence existingName = uniqueCharacters.get(charPrefix); + if (existingName != null) { + uniqueNames.put(existingName, findNameSplit(existingName)); + uniqueCharacters.put(charPrefix, null); + } + uniqueNames.put(senderName, findNameSplit(senderName)); + } else { + uniqueNames.put(senderName, charPrefix); + uniqueCharacters.put(charPrefix, senderName); + } + } + } + return uniqueNames; + } + + /** + * Update whether the groups can hide the sender if they are first + * (happens only for 1:1 conversations where the given title matches the sender's name) + */ + public void maybeHideFirstSenderName(@NonNull List<MessagingGroup> groups, + boolean isOneToOne, @Nullable CharSequence conversationTitle) { + for (int i = groups.size() - 1; i >= 0; i--) { + MessagingGroup messagingGroup = groups.get(i); + CharSequence messageSender = messagingGroup.getSenderName(); + boolean canHide = isOneToOne && TextUtils.equals(conversationTitle, messageSender); + messagingGroup.setCanHideSenderIfFirst(canHide); + } + } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index b3427551ea7b..ffba628f73ab 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1562,6 +1562,13 @@ static void nativeWriteTransactionToParcel(JNIEnv* env, jclass clazz, jlong nati reinterpret_cast<SurfaceComposerClient::Transaction *>(nativeObject); if (self != nullptr) { self->writeToParcel(parcel); + } +} + +static void nativeClearTransaction(JNIEnv* env, jclass clazz, jlong nativeObject) { + SurfaceComposerClient::Transaction* const self = + reinterpret_cast<SurfaceComposerClient::Transaction*>(nativeObject); + if (self != nullptr) { self->clear(); } } @@ -1880,6 +1887,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeReadTransactionFromParcel }, {"nativeWriteTransactionToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteTransactionToParcel }, + {"nativeClearTransaction", "(J)V", + (void*)nativeClearTransaction }, {"nativeMirrorSurface", "(J)J", (void*)nativeMirrorSurface }, {"nativeSetGlobalShadowSettings", "([F[FFFF)V", diff --git a/core/proto/android/internal/binder_latency.proto b/core/proto/android/internal/binder_latency.proto new file mode 100644 index 000000000000..e32c3e3441c5 --- /dev/null +++ b/core/proto/android/internal/binder_latency.proto @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2021 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. + */ + +syntax = "proto2"; +package com.android.internal.os; + +option java_outer_classname = "BinderLatencyProto"; + +/** + * RepeatedApiStats proto from atoms.proto, duplicated here so that it's + * accessible in the build. + * Must be kept in sync with the version in atoms.proto. + */ + +message RepeatedApiStats { + repeated ApiStats api_stats = 1; +} + +message Dims { + enum ProcessSource { + UNKNOWN_PROCESS_SOURCE = 0; + SYSTEM_SERVER = 1; + TELEPHONY = 2; + } + + enum ServiceClassName { + UNKNOWN_CLASS = 0; + } + enum ServiceMethodName { + UNKNOWN_METHOD = 0; + } + + // Required. + optional ProcessSource process_source = 1; + + // The class name of the API making the call to Binder. Enum value + // is preferred as uses much less data to store. + // This field does not contain PII. + oneof service_class { + ServiceClassName service_class_name_as_enum = 2; + string service_class_name = 3; + } + + // Method name of the API call. It can also be a transaction code if we + // cannot resolve it to a name. See Binder#getTransactionName. Enum value + // is preferred as uses much less data to store. + // This field does not contain PII. + oneof service_method { + ServiceMethodName service_method_name_as_enum = 4; + string service_method_name = 5; + } +} + +message ApiStats { + // required. + optional Dims dims = 1; + + // Indicates the first bucket that had any data. Allows omitting any empty + // buckets at the start of the bucket list and thus save on data size. + optional int32 first_bucket_index = 2; + // Stores the count of samples for each bucket. The number of buckets and + // their sizes are controlled server side with a flag. + repeated int32 buckets = 3; +}
\ No newline at end of file diff --git a/core/res/res/drawable/ic_call_answer_video.xml b/core/res/res/drawable/ic_call_answer_video.xml new file mode 100644 index 000000000000..77c889234dd0 --- /dev/null +++ b/core/res/res/drawable/ic_call_answer_video.xml @@ -0,0 +1,27 @@ +<!-- + Copyright (C) 2021 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="20" + android:viewportHeight="20" + android:tint="?android:attr/colorControlNormal" + android:autoMirrored="true"> + <path + android:fillColor="#FF000000" + android:pathData="M15,8v8H5V8h10m1,-2H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 + 1,-0.45 1,-1v-3.5l4,4v-11l-4,4V7c0,-0.55 -0.45,-1 -1,-1z"/> +</vector>
\ No newline at end of file diff --git a/core/res/res/layout/notification_expand_button.xml b/core/res/res/layout/notification_expand_button.xml index b969fa49bcc7..e752431ce75f 100644 --- a/core/res/res/layout/notification_expand_button.xml +++ b/core/res/res/layout/notification_expand_button.xml @@ -22,7 +22,6 @@ android:layout_gravity="top|end" android:contentDescription="@string/expand_button_content_description_collapsed" android:padding="16dp" - android:visibility="gone" > <LinearLayout @@ -40,6 +39,7 @@ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:gravity="center_vertical" android:paddingStart="8dp" + android:visibility="gone" /> <ImageView diff --git a/core/res/res/layout/notification_template_conversation_header.xml b/core/res/res/layout/notification_template_conversation_header.xml index e01d8035fe35..389637eb2517 100644 --- a/core/res/res/layout/notification_template_conversation_header.xml +++ b/core/res/res/layout/notification_template_conversation_header.xml @@ -20,7 +20,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingTop="16dp" + android:paddingTop="20dp" > <TextView @@ -42,8 +42,6 @@ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" android:text="@string/notification_header_divider_symbol" - android:layout_gravity="center" - android:paddingTop="1sp" android:singleLine="true" android:visibility="gone" /> @@ -54,10 +52,8 @@ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" - android:paddingTop="1sp" android:singleLine="true" android:visibility="gone" /> @@ -70,8 +66,6 @@ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" android:text="@string/notification_header_divider_symbol" - android:layout_gravity="center" - android:paddingTop="1sp" android:singleLine="true" android:visibility="gone" /> @@ -81,9 +75,7 @@ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" - android:paddingTop="1sp" android:showRelative="true" android:singleLine="true" android:visibility="gone" @@ -93,7 +85,6 @@ android:id="@+id/chronometer" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" android:layout="@layout/notification_template_part_chronometer" android:visibility="gone" @@ -107,8 +98,6 @@ android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin" android:text="@string/notification_header_divider_symbol" - android:layout_gravity="center" - android:paddingTop="1sp" android:singleLine="true" android:visibility="gone" /> @@ -117,9 +106,8 @@ android:id="@+id/verification_icon" android:layout_width="@dimen/notification_verification_icon_size" android:layout_height="@dimen/notification_verification_icon_size" - android:layout_gravity="center" android:layout_marginStart="4dp" - android:paddingTop="2dp" + android:baseline="10dp" android:scaleType="fitCenter" android:src="@drawable/ic_notifications_alerted" android:visibility="gone" @@ -130,9 +118,7 @@ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_marginStart="@dimen/notification_conversation_header_separating_margin" - android:paddingTop="1sp" android:showRelative="true" android:singleLine="true" android:visibility="gone" @@ -142,11 +128,10 @@ android:id="@+id/feedback" android:layout_width="@dimen/notification_feedback_size" android:layout_height="@dimen/notification_feedback_size" - android:layout_gravity="center" android:layout_marginStart="@dimen/notification_header_separating_margin" android:background="?android:selectableItemBackgroundBorderless" android:contentDescription="@string/notification_feedback_indicator" - android:paddingTop="2dp" + android:baseline="13dp" android:scaleType="fitCenter" android:src="@drawable/ic_feedback_indicator" android:visibility="gone" @@ -157,21 +142,19 @@ android:layout_width="@dimen/notification_phishing_alert_size" android:layout_height="@dimen/notification_phishing_alert_size" android:layout_marginStart="4dp" - android:paddingTop="2dp" + android:baseline="10dp" android:scaleType="fitCenter" android:src="@drawable/ic_dialog_alert_material" android:visibility="gone" android:contentDescription="@string/notification_phishing_alert_content_description" /> - <ImageView android:id="@+id/profile_badge" android:layout_width="@dimen/notification_badge_size" android:layout_height="@dimen/notification_badge_size" - android:layout_gravity="center" android:layout_marginStart="4dp" - android:paddingTop="2dp" + android:baseline="10dp" android:scaleType="fitCenter" android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" @@ -181,10 +164,9 @@ android:id="@+id/alerted_icon" android:layout_width="@dimen/notification_alerted_size" android:layout_height="@dimen/notification_alerted_size" - android:layout_gravity="center" android:layout_marginStart="4dp" + android:baseline="10dp" android:contentDescription="@string/notification_alerted_content_description" - android:paddingTop="2dp" android:scaleType="fitCenter" android:src="@drawable/ic_notifications_alerted" android:visibility="gone" diff --git a/core/res/res/layout/notification_template_conversation_icon_container.xml b/core/res/res/layout/notification_template_conversation_icon_container.xml index e9ec7ce77deb..a88ff0d104a6 100644 --- a/core/res/res/layout/notification_template_conversation_icon_container.xml +++ b/core/res/res/layout/notification_template_conversation_icon_container.xml @@ -23,8 +23,8 @@ android:gravity="start|top" android:clipChildren="false" android:clipToPadding="false" - android:paddingTop="12dp" - android:paddingBottom="12dp" + android:paddingTop="20dp" + android:paddingBottom="16dp" android:importantForAccessibility="no" > diff --git a/core/res/res/layout/notification_template_material_big_messaging.xml b/core/res/res/layout/notification_template_material_big_messaging.xml new file mode 100644 index 000000000000..01c37b7515e9 --- /dev/null +++ b/core/res/res/layout/notification_template_material_big_messaging.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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 + --> +<com.android.internal.widget.MessagingLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/status_bar_latest_event_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipToPadding="false" + android:clipChildren="false" + android:tag="messaging" + > + <include layout="@layout/notification_template_header"/> + <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:layout_marginTop="@dimen/notification_content_margin_top" + android:clipChildren="false" + android:orientation="vertical"> + + <com.android.internal.widget.RemeasuringLinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:layout_weight="1" + android:layout_marginEnd="@dimen/notification_content_margin_end" + android:orientation="vertical" + android:clipChildren="false" + > + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/notification_messaging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:spacing="@dimen/notification_messaging_spacing" /> + </com.android.internal.widget.RemeasuringLinearLayout> + <include layout="@layout/notification_template_smart_reply_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/notification_content_margin" + android:layout_marginStart="@dimen/notification_content_margin_start" + android:layout_marginEnd="@dimen/notification_content_margin_end" /> + <include layout="@layout/notification_material_action_list" /> + </com.android.internal.widget.RemeasuringLinearLayout> + <include layout="@layout/notification_template_right_icon" /> +</com.android.internal.widget.MessagingLayout> diff --git a/core/res/res/layout/notification_template_material_call.xml b/core/res/res/layout/notification_template_material_call.xml index 5d9e761842d8..1b3bd2673a7a 100644 --- a/core/res/res/layout/notification_template_material_call.xml +++ b/core/res/res/layout/notification_template_material_call.xml @@ -29,7 +29,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="80dp" + android:layout_height="88dp" android:orientation="horizontal" > diff --git a/core/res/res/layout/notification_template_material_heads_up_base.xml b/core/res/res/layout/notification_template_material_heads_up_base.xml index d55499130dcc..a0d19b409cea 100644 --- a/core/res/res/layout/notification_template_material_heads_up_base.xml +++ b/core/res/res/layout/notification_template_material_heads_up_base.xml @@ -38,7 +38,7 @@ <com.android.internal.widget.RemeasuringLinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="-12dp" + android:layout_marginTop="-20dp" android:clipChildren="false" android:orientation="vertical" > diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml index c3fd249d3ad5..3564f9755a5d 100644 --- a/core/res/res/layout/notification_template_material_messaging.xml +++ b/core/res/res/layout/notification_template_material_messaging.xml @@ -22,32 +22,186 @@ android:clipChildren="false" android:tag="messaging" > - <include layout="@layout/notification_template_header"/> - <com.android.internal.widget.RemeasuringLinearLayout - android:id="@+id/notification_action_list_margin_target" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="top" - android:layout_marginTop="@dimen/notification_content_margin_top" - android:clipToPadding="false" - android:orientation="vertical"> - <com.android.internal.widget.RemeasuringLinearLayout - android:id="@+id/notification_main_column" + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:orientation="vertical" + > + + + <com.android.internal.widget.NotificationMaxHeightFrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="top" - android:layout_weight="1" - android:layout_marginStart="@dimen/notification_content_margin_start" - android:layout_marginEnd="@dimen/notification_content_margin_end" - android:orientation="vertical" + android:minHeight="@dimen/notification_min_height" + android:clipChildren="false" > - <com.android.internal.widget.MessagingLinearLayout - android:id="@+id/notification_messaging" + + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_left_icon_size" + android:layout_height="@dimen/notification_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + + <com.android.internal.widget.CachingIconView + android:id="@+id/icon" + android:layout_width="@dimen/notification_icon_circle_size" + android:layout_height="@dimen/notification_icon_circle_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_icon_circle_start" + android:background="@drawable/notification_icon_circle" + android:padding="@dimen/notification_icon_circle_padding" + /> + + <FrameLayout + android:id="@+id/alternate_expand_target" + android:layout_width="@dimen/notification_content_margin_start" + android:layout_height="match_parent" + android:layout_gravity="start" + android:importantForAccessibility="no" + /> + + <!-- + NOTE: to make the expansion animation of id/notification_messaging happen vertically, + its X positioning must be the left edge of the notification, so instead of putting the + layout_marginStart on the id/notification_headerless_view_row, we put it on + id/notification_top_line, making the layout here just a bit different from the base. + --> + <LinearLayout + android:id="@+id/notification_headerless_view_row" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:spacing="@dimen/notification_messaging_spacing" /> - </com.android.internal.widget.RemeasuringLinearLayout> + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipChildren="false" + > + + <!-- + NOTE: because messaging will always have 2 lines, this LinearLayout should NOT + have the id/notification_headerless_view_column, as that is used for modifying + vertical margins to accommodate the single-line state that base supports + --> + <LinearLayout + android:layout_width="0px" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_weight="1" + android:layout_marginBottom="@dimen/notification_headerless_margin_twoline" + android:layout_marginTop="@dimen/notification_headerless_margin_twoline" + android:clipChildren="false" + android:orientation="vertical" + > + + <NotificationTopLineView + android:id="@+id/notification_top_line" + android:layout_width="wrap_content" + android:layout_height="@dimen/notification_headerless_line_height" + android:layout_marginStart="@dimen/notification_content_margin_start" + android:clipChildren="false" + android:theme="@style/Theme.DeviceDefault.Notification" + > + + <!-- + NOTE: The notification_top_line_views layout contains the app_name_text. + In order to include the title view at the beginning, the Notification.Builder + has logic to hide that view whenever this title view is to be visible. + --> + + <TextView + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/notification_header_separating_margin" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:singleLine="true" + android:textAlignment="viewStart" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" + /> + + <include layout="@layout/notification_top_line_views" /> + + </NotificationTopLineView> + + <LinearLayout + android:id="@+id/notification_main_column" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:clipChildren="false" + > + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/notification_messaging" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clipChildren="false" + android:spacing="@dimen/notification_messaging_spacing" /> + </LinearLayout> + + </LinearLayout> + + <!-- Images --> + <com.android.internal.widget.MessagingLinearLayout + android:id="@+id/conversation_image_message_container" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:forceHasOverlappingRendering="false" + android:spacing="0dp" + android:clipChildren="false" + android:visibility="gone" + /> + + <ImageView + android:id="@+id/right_icon" + android:layout_width="@dimen/notification_right_icon_size" + android:layout_height="@dimen/notification_right_icon_size" + android:layout_gravity="center_vertical|end" + android:layout_marginTop="@dimen/notification_right_icon_headerless_margin" + android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin" + android:layout_marginStart="@dimen/notification_right_icon_content_margin" + android:background="@drawable/notification_large_icon_outline" + android:clipToOutline="true" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + /> + + <FrameLayout + android:id="@+id/expand_button_touch_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:minWidth="@dimen/notification_content_margin_end" + > + + <include layout="@layout/notification_expand_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" + /> + + </FrameLayout> + + </LinearLayout> + + </com.android.internal.widget.NotificationMaxHeightFrameLayout> + + <LinearLayout + android:id="@+id/notification_action_list_margin_target" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="-20dp" + android:clipChildren="false" + android:orientation="vertical"> <include layout="@layout/notification_template_smart_reply_container" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -55,6 +209,6 @@ android:layout_marginStart="@dimen/notification_content_margin_start" android:layout_marginEnd="@dimen/notification_content_margin_end" /> <include layout="@layout/notification_material_action_list" /> - </com.android.internal.widget.RemeasuringLinearLayout> - <include layout="@layout/notification_template_right_icon" /> + </LinearLayout> +</LinearLayout> </com.android.internal.widget.MessagingLayout> diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml index 88bcc4dc9e39..8284279be3a0 100644 --- a/core/res/res/layout/notification_top_line_views.xml +++ b/core/res/res/layout/notification_top_line_views.xml @@ -113,11 +113,10 @@ android:layout_width="@dimen/notification_feedback_size" android:layout_height="@dimen/notification_feedback_size" android:layout_marginStart="@dimen/notification_header_separating_margin" - android:layout_gravity="center" + android:baseline="13dp" android:scaleType="fitCenter" android:src="@drawable/ic_feedback_indicator" android:background="?android:selectableItemBackgroundBorderless" - android:paddingTop="2dp" android:visibility="gone" android:contentDescription="@string/notification_feedback_indicator" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 906a7403298f..419f142d0795 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1772,6 +1772,9 @@ <!-- Add algorithm here --> </string-array> + <!-- Boolean indicating if placing the phone face down will result in a screen off. --> + <bool name="config_flipToScreenOffEnabled">true</bool> + <!-- Boolean indicating if current platform supports bluetooth SCO for off call use cases --> <bool name="config_bluetooth_sco_off_call">true</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index fc2645c7c88b..0e436e36b474 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -758,23 +758,23 @@ <dimen name="notification_grayscale_icon_max_size">256dp</dimen> <dimen name="messaging_avatar_size">36dp</dimen> - <dimen name="conversation_avatar_size">52dp</dimen> + <dimen name="conversation_avatar_size">48dp</dimen> <!-- start margin of the icon circle in the conversation's skin of the header --> <dimen name="conversation_icon_circle_start">28dp</dimen> <!-- Start of the content in the conversation template --> <dimen name="conversation_content_start">80dp</dimen> <!-- Height of the expand button in the conversation layout --> - <dimen name="conversation_expand_button_height">80dp</dimen> + <dimen name="conversation_expand_button_height">88dp</dimen> <!-- this is the margin between the Conversation image and the content --> <dimen name="conversation_image_start_margin">12dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon --> - <dimen name="conversation_badge_side_margin">36dp</dimen> + <dimen name="conversation_badge_side_margin">32dp</dimen> <!-- size of the notification badge when applied to the conversation icon --> <dimen name="conversation_icon_size_badged">20dp</dimen> <!-- size of the conversation avatar in an expanded group --> <dimen name="conversation_avatar_size_group_expanded">@dimen/messaging_avatar_size</dimen> <!-- size of the face pile icons --> - <dimen name="conversation_face_pile_avatar_size">@dimen/messaging_avatar_size</dimen> + <dimen name="conversation_face_pile_avatar_size">32dp</dimen> <!-- size of the face pile icons when the group is expanded --> <dimen name="conversation_face_pile_avatar_size_group_expanded">25dp</dimen> <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded--> @@ -795,7 +795,7 @@ <dimen name="importance_ring_size">20dp</dimen> <!-- The top padding of the conversation icon container in the regular state--> - <dimen name="conversation_icon_container_top_padding">12dp</dimen> + <dimen name="conversation_icon_container_top_padding">20dp</dimen> <!-- The top padding of the conversation icon container when the avatar is small--> <dimen name="conversation_icon_container_top_padding_small_avatar">9dp</dimen> @@ -803,8 +803,8 @@ <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end --> <dimen name="conversation_header_expanded_padding_end">38dp</dimen> - <!-- margin at the end of messaging group icons when not conversations --> - <dimen name="messaging_layout_margin_end">12dp</dimen> + <!-- extra padding at the start of the icons when not conversations to keep them horizontally aligned with the notification icon --> + <dimen name="messaging_layout_icon_padding_start">4dp</dimen> <!-- Padding between text and sender when singleline --> <dimen name="messaging_group_singleline_sender_padding_end">4dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 3208dff1d9f6..8d07ae254ee1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5151,6 +5151,8 @@ <!-- Action text to be displayed for the "answer" action of an incoming call [CHAR LIMIT=13] --> <string name="call_notification_answer_action">Answer</string> + <!-- Action text to be displayed for the "answer" action of an incoming VIDEO call [CHAR LIMIT=13] --> + <string name="call_notification_answer_video_action">Video</string> <!-- Action text to be displayed for the "decline" action of an incoming call [CHAR LIMIT=13] --> <string name="call_notification_decline_action">Decline</string> <!-- Action text to be displayed for the "hang up" action of an ongoing call [CHAR LIMIT=13] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3de2ac108021..d69c7f8f9587 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -275,6 +275,7 @@ <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" /> <java-symbol type="bool" name="config_avoidGfxAccel" /> <java-symbol type="bool" name="config_bluetooth_address_validation" /> + <java-symbol type="bool" name="config_flipToScreenOffEnabled" /> <java-symbol type="bool" name="config_bluetooth_sco_off_call" /> <java-symbol type="bool" name="config_bluetooth_le_peripheral_mode_supported" /> <java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" /> @@ -3015,6 +3016,7 @@ <java-symbol type="layout" name="app_anr_dialog" /> <java-symbol type="layout" name="notification_template_material_messaging" /> + <java-symbol type="layout" name="notification_template_material_big_messaging" /> <java-symbol type="id" name="aerr_wait" /> @@ -3115,6 +3117,7 @@ <java-symbol type="layout" name="notification_template_material_call" /> <java-symbol type="layout" name="notification_template_material_big_call" /> <java-symbol type="string" name="call_notification_answer_action" /> + <java-symbol type="string" name="call_notification_answer_video_action" /> <java-symbol type="string" name="call_notification_decline_action" /> <java-symbol type="string" name="call_notification_hang_up_action" /> <java-symbol type="string" name="call_notification_incoming_text" /> @@ -3124,6 +3127,7 @@ <java-symbol type="color" name="call_notification_answer_color"/> <java-symbol type="dimen" name="call_notification_collapsible_indent"/> <java-symbol type="drawable" name="ic_call_answer" /> + <java-symbol type="drawable" name="ic_call_answer_video" /> <java-symbol type="drawable" name="ic_call_decline" /> <java-symbol type="id" name="verification_divider" /> <java-symbol type="id" name="verification_icon" /> @@ -3645,6 +3649,7 @@ <java-symbol type="id" name="bubble_button" /> <java-symbol type="id" name="snooze_button" /> <java-symbol type="dimen" name="text_size_body_2_material" /> + <java-symbol type="dimen" name="notification_icon_circle_size" /> <java-symbol type="dimen" name="messaging_avatar_size" /> <java-symbol type="dimen" name="messaging_group_sending_progress_size" /> <java-symbol type="dimen" name="messaging_image_rounding" /> @@ -4061,7 +4066,7 @@ <java-symbol type="dimen" name="conversation_badge_side_margin_group_expanded_face_pile" /> <java-symbol type="dimen" name="conversation_content_start" /> <java-symbol type="dimen" name="expanded_group_conversation_message_padding" /> - <java-symbol type="dimen" name="messaging_layout_margin_end" /> + <java-symbol type="dimen" name="messaging_layout_icon_padding_start" /> <java-symbol type="dimen" name="conversation_header_expanded_padding_end" /> <java-symbol type="dimen" name="conversation_icon_container_top_padding" /> <java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" /> @@ -4335,4 +4340,6 @@ <java-symbol type="drawable" name="ic_accessibility_24dp" /> <java-symbol type="string" name="view_and_control_notification_title" /> <java-symbol type="string" name="view_and_control_notification_content" /> + + <java-symbol type="layout" name="notification_expand_button"/> </resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml new file mode 100644 index 000000000000..1f5731821c94 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#d14d2c"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11.17,19.5h-0.83l0.82,-5.83 -4.18,0.01 5.85,-9.17h0.83l-0.84,5.84h4.17l-5.82,9.15z"/> +</vector> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_calculate_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_calculate_24.xml new file mode 100644 index 000000000000..70aac32227c4 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_calculate_24.xml @@ -0,0 +1,25 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#269e5c"> + <path + android:fillColor="@android:color/white" + android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM19,19H5V5h14V19z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M6.25,7.72h5v1.5h-5z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M13,15.75h5v1.5h-5z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M13,13.25h5v1.5h-5z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M8,18l1.5,0l0,-2l2,0l0,-1.5l-2,0l0,-2l-1.5,0l0,2l-2,0l0,1.5l2,0z"/> + <path + android:fillColor="@android:color/white" + android:pathData="M14.09,10.95l1.41,-1.41l1.41,1.41l1.06,-1.06l-1.41,-1.42l1.41,-1.41l-1.06,-1.06l-1.41,1.41l-1.41,-1.41l-1.06,1.06l1.41,1.41l-1.41,1.42z"/> +</vector> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml new file mode 100644 index 000000000000..39f9689d2ec4 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#d14d2c"> + <path + android:fillColor="@android:color/white" + android:pathData="M22,9L22,7h-2L20,5c0,-1.1 -0.9,-2 -2,-2L4,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2v-2h-2L20,9h2zM18,19L4,19L4,5h14v14zM6,13h5v4L6,17v-4zM12,7h4v3h-4L12,7zM6,7h5v5L6,12L6,7zM12,11h4v6h-4v-6z"/> +</vector> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_timer_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_timer_24.xml new file mode 100644 index 000000000000..9cae545e165b --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_timer_24.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M15,3L9,3L9,1h6v2zM11,14h2L13,8h-2v6zM21,13.01c0,4.97 -4.02,9 -9,9s-9,-4.03 -9,-9 4.03,-9 9,-9c2.12,0 4.07,0.74 5.62,1.98l1.42,-1.42c0.51,0.42 0.98,0.9 1.41,1.41L19.03,7.4C20.26,8.93 21,10.89 21,13.01zM19,13.01c0,-3.87 -3.13,-7 -7,-7s-7,3.13 -7,7 3.13,7 7,7 7,-3.13 7,-7z"/> +</vector> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml index 1ced825adf31..98fc581f3420 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_entry_layout.xml @@ -25,6 +25,13 @@ android:paddingTop="8dp" android:paddingBottom="8dp"> + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginEnd="8dp"/> + <TextView android:id="@+id/title" android:layout_width="0dp" @@ -34,16 +41,18 @@ <TextView android:id="@+id/amount" - android:layout_width="0dp" - android:layout_weight="0.7" + android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginStart="8dp" android:gravity="right" + android:maxLines="1" android:textAppearance="@style/TextAppearanceBody"/> <TextView android:id="@+id/percent" - android:layout_width="64dp" + android:layout_width="76dp" android:layout_height="wrap_content" android:gravity="right" + android:maxLines="1" android:textAppearance="@style/TextAppearanceBody"/> </LinearLayout> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml index e58a08fd362c..24d193c49219 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml @@ -43,10 +43,40 @@ android:paddingEnd="10dp"> <include layout="@layout/battery_consumer_info_layout"/> - </LinearLayout> + </androidx.cardview.widget.CardView> + + <LinearLayout + android:id="@+id/headings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="2dp" + android:paddingBottom="4dp"> + <FrameLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"/> + <TextView + android:layout_width="100dp" + android:layout_height="wrap_content" + android:gravity="end" + android:paddingEnd="10dp" + android:text="Total"/> + <TextView + android:layout_width="100dp" + android:layout_height="wrap_content" + android:gravity="end" + android:paddingEnd="30dp" + android:text="Apps"/> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@android:color/darker_gray"/> + <androidx.recyclerview.widget.RecyclerView android:id="@+id/battery_consumer_data_view" android:layout_width="match_parent" diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml new file mode 100644 index 000000000000..2dbe48b6edc0 --- /dev/null +++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> + +<resources> + <color name="battery_consumer_bg_power_profile">#ffffff</color> + <color name="battery_consumer_bg_measured_energy">#fff5eb</color> +</resources> diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java index 78569bdb4adc..f7d7098b1726 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java @@ -18,32 +18,21 @@ package com.android.frameworks.core.batterystatsviewer; import android.content.Context; import android.os.BatteryConsumer; -import android.os.BatteryStats; import android.os.BatteryUsageStats; -import android.os.Process; import android.os.SystemBatteryConsumer; import android.os.UidBatteryConsumer; import android.os.UserHandle; - -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; +import android.util.DebugUtils; import java.util.ArrayList; import java.util.List; public class BatteryConsumerData { - private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar"; - private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media"; - private static final String PACKAGE_SYSTEMUI = "com.android.systemui"; - private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, - PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; - - // Unit conversion: - // mAh = uC * (1/1000)(milli/micro) * (1/3600)(hours/second) - private static final double UC_2_MAH = (1.0 / 1000) * (1.0 / 3600); enum EntryType { - POWER, + POWER_MODELED, + POWER_MEASURED, + POWER_CUSTOM, DURATION, } @@ -52,268 +41,155 @@ public class BatteryConsumerData { public EntryType entryType; public double value; public double total; + public boolean isSystemBatteryConsumer; } private final BatteryConsumerInfoHelper.BatteryConsumerInfo mBatteryConsumerInfo; private final List<Entry> mEntries = new ArrayList<>(); - public BatteryConsumerData(Context context, BatteryStatsHelper batteryStatsHelper, + public BatteryConsumerData(Context context, List<BatteryUsageStats> batteryUsageStatsList, String batteryConsumerId) { BatteryUsageStats batteryUsageStats = batteryUsageStatsList.get(0); - BatteryUsageStats powerProfileModeledUsageStats = batteryUsageStatsList.get(1); - List<BatterySipper> usageList = batteryStatsHelper.getUsageList(); - BatteryStats batteryStats = batteryStatsHelper.getStats(); - - double totalPowerMah = 0; - double totalSmearedPowerMah = 0; - double totalPowerExcludeSystemMah = 0; - double totalScreenPower = 0; - double totalProportionalSmearMah = 0; - double totalCpuPowerMah = 0; - double totalSystemServiceCpuPowerMah = 0; - double totalUsagePowerMah = 0; - double totalWakeLockPowerMah = 0; - double totalMobileRadioPowerMah = 0; - double totalWifiPowerMah = 0; - double totalBluetoothPowerMah = 0; - double totalGpsPowerMah = 0; - double totalCameraPowerMah = 0; - double totalFlashlightPowerMah = 0; - double totalSensorPowerMah = 0; - double totalAudioPowerMah = 0; - double totalVideoPowerMah = 0; + BatteryUsageStats modeledBatteryUsageStats = batteryUsageStatsList.get(1); - long totalCpuTimeMs = 0; - long totalCpuFgTimeMs = 0; - long totalWakeLockTimeMs = 0; - long totalWifiRunningTimeMs = 0; - long totalBluetoothRunningTimeMs = 0; - long totalGpsTimeMs = 0; - long totalCameraTimeMs = 0; - long totalFlashlightTimeMs = 0; - long totalAudioTimeMs = 0; - long totalVideoTimeMs = 0; + BatteryConsumer requestedBatteryConsumer = getRequestedBatteryConsumer(batteryUsageStats, + batteryConsumerId); + BatteryConsumer requestedModeledBatteryConsumer = getRequestedBatteryConsumer( + modeledBatteryUsageStats, batteryConsumerId); - BatterySipper requestedBatterySipper = null; - for (BatterySipper sipper : usageList) { - if (sipper.drainType == BatterySipper.DrainType.SCREEN) { - totalScreenPower = sipper.sumPower(); - } - - if (batteryConsumerId(sipper).equals(batteryConsumerId)) { - requestedBatterySipper = sipper; - } - - totalPowerMah += sipper.sumPower(); - totalSmearedPowerMah += sipper.totalSmearedPowerMah; - totalProportionalSmearMah += sipper.proportionalSmearMah; + if (requestedBatteryConsumer == null || requestedModeledBatteryConsumer == null) { + mBatteryConsumerInfo = null; + return; + } - if (!isSystemSipper(sipper)) { - totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah; + mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo( + context.getPackageManager(), requestedBatteryConsumer); + + double[] totalPowerByComponentMah = new double[BatteryConsumer.POWER_COMPONENT_COUNT]; + double[] totalModeledPowerByComponentMah = + new double[BatteryConsumer.POWER_COMPONENT_COUNT]; + long[] totalDurationByComponentMs = new long[BatteryConsumer.TIME_COMPONENT_COUNT]; + final int customComponentCount = + requestedBatteryConsumer.getCustomPowerComponentCount(); + double[] totalCustomPowerByComponentMah = new double[customComponentCount]; + + computeTotalPower(batteryUsageStats, totalPowerByComponentMah); + computeTotalPower(modeledBatteryUsageStats, totalModeledPowerByComponentMah); + computeTotalPowerForCustomComponent(batteryUsageStats, totalCustomPowerByComponentMah); + computeTotalDuration(batteryUsageStats, totalDurationByComponentMs); + + for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; component++) { + final String metricTitle = getPowerMetricTitle(component); + final int powerModel = requestedBatteryConsumer.getPowerModel(component); + if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) { + addEntry(metricTitle, EntryType.POWER_MODELED, + requestedBatteryConsumer.getConsumedPower(component), + totalPowerByComponentMah[component], + mBatteryConsumerInfo.isSystemBatteryConsumer); + } else { + addEntry(metricTitle + " (measured)", EntryType.POWER_MEASURED, + requestedBatteryConsumer.getConsumedPower(component), + totalPowerByComponentMah[component], + mBatteryConsumerInfo.isSystemBatteryConsumer); + addEntry(metricTitle + " (modeled)", EntryType.POWER_MODELED, + requestedModeledBatteryConsumer.getConsumedPower(component), + totalModeledPowerByComponentMah[component], + mBatteryConsumerInfo.isSystemBatteryConsumer); } + } - totalCpuPowerMah += sipper.cpuPowerMah; - totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah; - totalUsagePowerMah += sipper.usagePowerMah; - totalWakeLockPowerMah += sipper.wakeLockPowerMah; - totalMobileRadioPowerMah += sipper.mobileRadioPowerMah; - totalWifiPowerMah += sipper.wifiPowerMah; - totalBluetoothPowerMah += sipper.bluetoothPowerMah; - totalGpsPowerMah += sipper.gpsPowerMah; - totalCameraPowerMah += sipper.cameraPowerMah; - totalFlashlightPowerMah += sipper.flashlightPowerMah; - totalSensorPowerMah += sipper.sensorPowerMah; - totalAudioPowerMah += sipper.audioPowerMah; - totalVideoPowerMah += sipper.videoPowerMah; - - totalCpuTimeMs += sipper.cpuTimeMs; - totalCpuFgTimeMs += sipper.cpuFgTimeMs; - totalWakeLockTimeMs += sipper.wakeLockTimeMs; - totalWifiRunningTimeMs += sipper.wifiRunningTimeMs; - totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs; - totalGpsTimeMs += sipper.gpsTimeMs; - totalCameraTimeMs += sipper.cameraTimeMs; - totalFlashlightTimeMs += sipper.flashlightTimeMs; - totalAudioTimeMs += sipper.audioTimeMs; - totalVideoTimeMs += sipper.videoTimeMs; + for (int component = 0; component < customComponentCount; component++) { + final String name = requestedBatteryConsumer.getCustomPowerComponentName( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component); + addEntry(name + " (custom)", EntryType.POWER_CUSTOM, + requestedBatteryConsumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component), + totalCustomPowerByComponentMah[component], + mBatteryConsumerInfo.isSystemBatteryConsumer); } - BatteryConsumer requestedBatteryConsumer = null; + for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; component++) { + final String metricTitle = getTimeMetricTitle(component); + addEntry(metricTitle, EntryType.DURATION, + requestedBatteryConsumer.getUsageDurationMillis(component), + totalDurationByComponentMs[component], + mBatteryConsumerInfo.isSystemBatteryConsumer); + } + } + private BatteryConsumer getRequestedBatteryConsumer(BatteryUsageStats batteryUsageStats, + String batteryConsumerId) { for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { if (batteryConsumerId(consumer).equals(batteryConsumerId)) { - requestedBatteryConsumer = consumer; + return consumer; } } - - double totalModeledCpuPowerMah = 0; - BatteryConsumer requestedBatteryConsumerPowerProfileModeled = null; - for (BatteryConsumer consumer : powerProfileModeledUsageStats.getUidBatteryConsumers()) { + for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) { if (batteryConsumerId(consumer).equals(batteryConsumerId)) { - requestedBatteryConsumerPowerProfileModeled = consumer; + return consumer; } - - totalModeledCpuPowerMah += consumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU); } + return null; + } - if (requestedBatterySipper == null) { - mBatteryConsumerInfo = null; - return; - } + static String getPowerMetricTitle(int componentId) { + final String componentName = DebugUtils.constantToString(BatteryConsumer.class, + "POWER_COMPONENT_", componentId); + return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ') + + " power"; + } + + static String getTimeMetricTitle(int componentId) { + final String componentName = DebugUtils.constantToString(BatteryConsumer.class, + "TIME_COMPONENT_", componentId); + return componentName.charAt(0) + componentName.substring(1).toLowerCase().replace('_', ' ') + + " time"; + } - if (requestedBatteryConsumer == null) { - for (BatteryConsumer consumer : batteryUsageStats.getSystemBatteryConsumers()) { - if (batteryConsumerId(consumer).equals(batteryConsumerId)) { - requestedBatteryConsumer = consumer; - break; - } + private void computeTotalPower(BatteryUsageStats batteryUsageStats, + double[] powerByComponentMah) { + for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { + for (int component = 0; component < BatteryConsumer.POWER_COMPONENT_COUNT; + component++) { + powerByComponentMah[component] += consumer.getConsumedPower(component); } } + } - mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo( - context.getPackageManager(), requestedBatterySipper); - long totalScreenMeasuredChargeUC = - batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); - long uidScreenMeasuredChargeUC = - requestedBatterySipper.uidObj.getScreenOnMeasuredBatteryConsumptionUC(); - - addEntry("Total power", EntryType.POWER, - requestedBatterySipper.totalSmearedPowerMah, totalSmearedPowerMah); - maybeAddMeasuredEnergyEntry(requestedBatterySipper.drainType, batteryStats); - - addEntry("... excluding system", EntryType.POWER, - requestedBatterySipper.totalSmearedPowerMah, totalPowerExcludeSystemMah); - addEntry("Screen, smeared", EntryType.POWER, - requestedBatterySipper.screenPowerMah, totalScreenPower); - if (uidScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE - && totalScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) { - final double measuredCharge = UC_2_MAH * uidScreenMeasuredChargeUC; - final double totalMeasuredCharge = UC_2_MAH * totalScreenMeasuredChargeUC; - addEntry("Screen, measured", EntryType.POWER, - measuredCharge, totalMeasuredCharge); - } - addEntry("Other, smeared", EntryType.POWER, - requestedBatterySipper.proportionalSmearMah, totalProportionalSmearMah); - addEntry("Excluding smeared", EntryType.POWER, - requestedBatterySipper.totalPowerMah, totalPowerMah); - if (requestedBatteryConsumer != null) { - addEntry("CPU", EntryType.POWER, - requestedBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU), - totalCpuPowerMah); - if (requestedBatteryConsumerPowerProfileModeled != null) { - addEntry("CPU (modeled)", EntryType.POWER, - requestedBatteryConsumerPowerProfileModeled.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_CPU), - totalModeledCpuPowerMah); + private void computeTotalDuration(BatteryUsageStats batteryUsageStats, + long[] durationByComponentMs) { + for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { + for (int component = 0; component < BatteryConsumer.TIME_COMPONENT_COUNT; + component++) { + durationByComponentMs[component] += consumer.getUsageDurationMillis(component); } - } else { - addEntry("CPU (sipper)", EntryType.POWER, - requestedBatterySipper.cpuPowerMah, totalCpuPowerMah); } - addEntry("System services", EntryType.POWER, - requestedBatterySipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah); - if (requestedBatteryConsumer != null) { - addEntry("Usage", EntryType.POWER, - requestedBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_USAGE), totalUsagePowerMah); - } else { - addEntry("Usage (sipper)", EntryType.POWER, - requestedBatterySipper.usagePowerMah, totalUsagePowerMah); - } - addEntry("Wake lock", EntryType.POWER, - requestedBatterySipper.wakeLockPowerMah, totalWakeLockPowerMah); - addEntry("Mobile radio", EntryType.POWER, - requestedBatterySipper.mobileRadioPowerMah, totalMobileRadioPowerMah); - addEntry("WiFi", EntryType.POWER, - requestedBatterySipper.wifiPowerMah, totalWifiPowerMah); - addEntry("Bluetooth", EntryType.POWER, - requestedBatterySipper.bluetoothPowerMah, totalBluetoothPowerMah); - addEntry("GPS", EntryType.POWER, - requestedBatterySipper.gpsPowerMah, totalGpsPowerMah); - addEntry("Camera", EntryType.POWER, - requestedBatterySipper.cameraPowerMah, totalCameraPowerMah); - addEntry("Flashlight", EntryType.POWER, - requestedBatterySipper.flashlightPowerMah, totalFlashlightPowerMah); - addEntry("Sensors", EntryType.POWER, - requestedBatterySipper.sensorPowerMah, totalSensorPowerMah); - addEntry("Audio", EntryType.POWER, - requestedBatterySipper.audioPowerMah, totalAudioPowerMah); - addEntry("Video", EntryType.POWER, - requestedBatterySipper.videoPowerMah, totalVideoPowerMah); - - addEntry("CPU time", EntryType.DURATION, - requestedBatterySipper.cpuTimeMs, totalCpuTimeMs); - addEntry("CPU foreground time", EntryType.DURATION, - requestedBatterySipper.cpuFgTimeMs, totalCpuFgTimeMs); - addEntry("Wake lock time", EntryType.DURATION, - requestedBatterySipper.wakeLockTimeMs, totalWakeLockTimeMs); - addEntry("WiFi running time", EntryType.DURATION, - requestedBatterySipper.wifiRunningTimeMs, totalWifiRunningTimeMs); - addEntry("Bluetooth time", EntryType.DURATION, - requestedBatterySipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs); - addEntry("GPS time", EntryType.DURATION, - requestedBatterySipper.gpsTimeMs, totalGpsTimeMs); - addEntry("Camera time", EntryType.DURATION, - requestedBatterySipper.cameraTimeMs, totalCameraTimeMs); - addEntry("Flashlight time", EntryType.DURATION, - requestedBatterySipper.flashlightTimeMs, totalFlashlightTimeMs); - addEntry("Audio time", EntryType.DURATION, - requestedBatterySipper.audioTimeMs, totalAudioTimeMs); - addEntry("Video time", EntryType.DURATION, - requestedBatterySipper.videoTimeMs, totalVideoTimeMs); } - private boolean isSystemSipper(BatterySipper sipper) { - final int uid = sipper.uidObj == null ? -1 : sipper.getUid(); - if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) { - return true; - } else if (sipper.mPackages != null) { - for (final String packageName : sipper.mPackages) { - for (final String systemPackage : PACKAGES_SYSTEM) { - if (systemPackage.equals(packageName)) { - return true; - } - } + private void computeTotalPowerForCustomComponent( + BatteryUsageStats batteryUsageStats, double[] powerByComponentMah) { + for (BatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { + final int customComponentCount = consumer.getCustomPowerComponentCount(); + for (int component = 0; + component < Math.min(customComponentCount, powerByComponentMah.length); + component++) { + powerByComponentMah[component] += consumer.getConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component); } } - - return false; } - private void addEntry(String title, EntryType entryType, double amount, double totalAmount) { + private void addEntry(String title, EntryType entryType, double amount, double totalAmount, + boolean isSystemBatteryConsumer) { Entry entry = new Entry(); entry.title = title; entry.entryType = entryType; entry.value = amount; entry.total = totalAmount; + entry.isSystemBatteryConsumer = isSystemBatteryConsumer; mEntries.add(entry); } - private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType, - BatteryStats batteryStats) { - switch (drainType) { - case AMBIENT_DISPLAY: - final long totalDozeMeasuredChargeUC = - batteryStats.getScreenDozeMeasuredBatteryConsumptionUC(); - if (totalDozeMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) { - final double measuredCharge = UC_2_MAH * totalDozeMeasuredChargeUC; - addEntry("Measured ambient display power", EntryType.POWER, measuredCharge, - measuredCharge); - } - break; - case SCREEN: - final long totalScreenMeasuredChargeUC = - batteryStats.getScreenOnMeasuredBatteryConsumptionUC(); - if (totalScreenMeasuredChargeUC != BatteryStats.POWER_DATA_UNAVAILABLE) { - final double measuredCharge = UC_2_MAH * totalScreenMeasuredChargeUC; - addEntry("Measured screen power", EntryType.POWER, measuredCharge, - measuredCharge); - } - break; - } - } - public BatteryConsumerInfoHelper.BatteryConsumerInfo getBatteryConsumerInfo() { return mBatteryConsumerInfo; } @@ -322,13 +198,9 @@ public class BatteryConsumerData { return mEntries; } - public static String batteryConsumerId(BatterySipper sipper) { - return sipper.drainType + "|" + sipper.userId + "|" + sipper.getUid(); - } - public static String batteryConsumerId(BatteryConsumer consumer) { if (consumer instanceof UidBatteryConsumer) { - return BatterySipper.DrainType.APP + "|" + return "APP|" + UserHandle.getUserId(((UidBatteryConsumer) consumer).getUid()) + "|" + ((UidBatteryConsumer) consumer).getUid(); } else if (consumer instanceof SystemBatteryConsumer) { diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java index 8ee6c604cb3e..6288e0b886d1 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerInfoHelper.java @@ -18,14 +18,14 @@ package com.android.frameworks.core.batterystatsviewer; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.BatteryConsumer; import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.util.DebugUtils; import androidx.annotation.NonNull; -import com.android.internal.os.BatterySipper; - -import java.util.Locale; - class BatteryConsumerInfoHelper { private static final String SYSTEM_SERVER_PACKAGE_NAME = "android"; @@ -37,111 +37,79 @@ class BatteryConsumerInfoHelper { public ApplicationInfo iconInfo; public CharSequence packages; public CharSequence details; + public boolean isSystemBatteryConsumer; } @NonNull public static BatteryConsumerInfo makeBatteryConsumerInfo(PackageManager packageManager, - @NonNull BatterySipper sipper) { + @NonNull BatteryConsumer batteryConsumer) { BatteryConsumerInfo info = new BatteryConsumerInfo(); - info.id = BatteryConsumerData.batteryConsumerId(sipper); - sipper.sumPower(); - info.powerMah = sipper.totalSmearedPowerMah; - switch (sipper.drainType) { - case APP: { - int uid = sipper.getUid(); - info.details = String.format("UID: %d", uid); - String packageWithHighestDrain = sipper.packageWithHighestDrain; - if (uid == Process.ROOT_UID) { - info.label = "<root>"; - } else { - String[] packages = packageManager.getPackagesForUid(uid); - String primaryPackageName = null; - if (uid == Process.SYSTEM_UID) { - primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME; - } else if (packages != null) { - for (String name : packages) { - primaryPackageName = name; - if (name.equals(packageWithHighestDrain)) { - break; - } + info.id = BatteryConsumerData.batteryConsumerId(batteryConsumer); + info.powerMah = batteryConsumer.getConsumedPower(); + + if (batteryConsumer instanceof UidBatteryConsumer) { + final UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer; + int uid = uidBatteryConsumer.getUid(); + info.details = String.format("UID: %d", uid); + String packageWithHighestDrain = uidBatteryConsumer.getPackageWithHighestDrain(); + if (uid == Process.ROOT_UID) { + info.label = "<root>"; + } else { + String[] packages = packageManager.getPackagesForUid(uid); + String primaryPackageName = null; + if (uid == Process.SYSTEM_UID) { + primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME; + } else if (packages != null) { + for (String name : packages) { + primaryPackageName = name; + if (name.equals(packageWithHighestDrain)) { + break; } } + } - if (primaryPackageName != null) { - try { - ApplicationInfo applicationInfo = - packageManager.getApplicationInfo(primaryPackageName, 0); - info.label = applicationInfo.loadLabel(packageManager); - info.iconInfo = applicationInfo; - } catch (PackageManager.NameNotFoundException e) { - info.label = primaryPackageName; - } - } else if (packageWithHighestDrain != null) { - info.label = packageWithHighestDrain; + if (primaryPackageName != null) { + try { + ApplicationInfo applicationInfo = + packageManager.getApplicationInfo(primaryPackageName, 0); + info.label = applicationInfo.loadLabel(packageManager); + info.iconInfo = applicationInfo; + } catch (PackageManager.NameNotFoundException e) { + info.label = primaryPackageName; } + } else if (packageWithHighestDrain != null) { + info.label = packageWithHighestDrain; + } - if (packages != null && packages.length > 0) { - StringBuilder sb = new StringBuilder(); - if (primaryPackageName != null) { - sb.append(primaryPackageName); + if (packages != null && packages.length > 0) { + StringBuilder sb = new StringBuilder(); + if (primaryPackageName != null) { + sb.append(primaryPackageName); + } + for (String packageName : packages) { + if (packageName.equals(primaryPackageName)) { + continue; } - for (String packageName : packages) { - if (packageName.equals(primaryPackageName)) { - continue; - } - if (sb.length() != 0) { - sb.append(", "); - } - sb.append(packageName); + if (sb.length() != 0) { + sb.append(", "); } - - info.packages = sb; + sb.append(packageName); } + + info.packages = sb; } - break; } - case USER: - info.label = "User"; - info.details = String.format(Locale.getDefault(), "User ID: %d", sipper.userId); - break; - case AMBIENT_DISPLAY: - info.label = "Ambient display"; - break; - case BLUETOOTH: - info.label = "Bluetooth"; - break; - case CAMERA: - info.label = "Camera"; - break; - case CELL: - info.label = "Cell"; - break; - case FLASHLIGHT: - info.label = "Flashlight"; - break; - case IDLE: - info.label = "Idle"; - break; - case MEMORY: - info.label = "Memory"; - break; - case OVERCOUNTED: - info.label = "Overcounted"; - break; - case PHONE: - info.label = "Phone"; - break; - case SCREEN: - info.label = "Screen"; - break; - case UNACCOUNTED: - info.label = "Unaccounted"; - break; - case WIFI: - info.label = "WiFi"; - break; + } else if (batteryConsumer instanceof SystemBatteryConsumer) { + final SystemBatteryConsumer systemBatteryConsumer = + (SystemBatteryConsumer) batteryConsumer; + final int drainType = systemBatteryConsumer.getDrainType(); + String name = DebugUtils.constantToString(SystemBatteryConsumer.class, "DRAIN_TYPE_", + drainType); + info.label = name.charAt(0) + name.substring(1).toLowerCase().replace('_', ' '); + info.isSystemBatteryConsumer = true; } + // Default the app icon to System Server. This includes root, dex2oat and other UIDs. if (info.iconInfo == null) { try { diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java index bb11fd598511..49220877d31e 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerPickerFragment.java @@ -18,10 +18,11 @@ package com.android.frameworks.core.batterystatsviewer; import android.content.Context; import android.content.pm.PackageManager; -import android.os.BatteryStats; +import android.os.BatteryStatsManager; +import android.os.BatteryUsageStats; import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -37,8 +38,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.android.frameworks.core.batterystatsviewer.BatteryConsumerInfoHelper.BatteryConsumerInfo; -import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.settingslib.utils.AsyncLoaderCompat; import java.util.ArrayList; @@ -99,44 +98,39 @@ public class BatteryConsumerPickerFragment extends Fragment { private static class BatteryConsumerListLoader extends AsyncLoaderCompat<List<BatteryConsumerInfo>> { - private final BatteryStatsHelper mStatsHelper; private final int mPickerType; - private final UserManager mUserManager; + private final BatteryStatsManager mBatteryStatsManager; private final PackageManager mPackageManager; BatteryConsumerListLoader(Context context, int pickerType) { super(context); - mUserManager = context.getSystemService(UserManager.class); - mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */); + mBatteryStatsManager = context.getSystemService(BatteryStatsManager.class); mPickerType = pickerType; - mStatsHelper.create((Bundle) null); - mStatsHelper.clearStats(); mPackageManager = context.getPackageManager(); } @Override public List<BatteryConsumerInfo> loadInBackground() { - List<BatteryConsumerInfo> batteryConsumerList = new ArrayList<>(); + final BatteryUsageStats batteryUsageStats = mBatteryStatsManager.getBatteryUsageStats(); - mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId()); - - final List<BatterySipper> usageList = mStatsHelper.getUsageList(); - for (BatterySipper sipper : usageList) { - switch (mPickerType) { - case PICKER_TYPE_APP: - if (sipper.drainType != BatterySipper.DrainType.APP) { - continue; - } - break; - case PICKER_TYPE_DRAIN: - default: - if (sipper.drainType == BatterySipper.DrainType.APP) { - continue; - } - } - - batteryConsumerList.add( - BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager, sipper)); + List<BatteryConsumerInfo> batteryConsumerList = new ArrayList<>(); + switch (mPickerType) { + case PICKER_TYPE_APP: + for (UidBatteryConsumer consumer : batteryUsageStats.getUidBatteryConsumers()) { + batteryConsumerList.add( + BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager, + consumer)); + } + break; + case PICKER_TYPE_DRAIN: + default: + for (SystemBatteryConsumer consumer : + batteryUsageStats.getSystemBatteryConsumers()) { + batteryConsumerList.add( + BatteryConsumerInfoHelper.makeBatteryConsumerInfo(mPackageManager, + consumer)); + } + break; } batteryConsumerList.sort( diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java index 4ead8eef5684..74d3fb336f40 100644 --- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java +++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java @@ -18,13 +18,10 @@ package com.android.frameworks.core.batterystatsviewer; import android.content.Context; import android.content.SharedPreferences; -import android.os.BatteryStats; import android.os.BatteryStatsManager; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -41,7 +38,6 @@ import androidx.loader.content.Loader; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.android.internal.os.BatteryStatsHelper; import com.android.settingslib.utils.AsyncLoaderCompat; import java.util.Collections; @@ -50,24 +46,24 @@ import java.util.Locale; public class BatteryStatsViewerActivity extends ComponentActivity { private static final int BATTERY_STATS_REFRESH_RATE_MILLIS = 60 * 1000; - public static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId"; - public static final int LOADER_BATTERY_STATS_HELPER = 0; - public static final int LOADER_BATTERY_USAGE_STATS = 1; + private static final int MILLIS_IN_MINUTE = 60000; + private static final String PREF_SELECTED_BATTERY_CONSUMER = "batteryConsumerId"; + private static final int LOADER_BATTERY_USAGE_STATS = 1; private BatteryStatsDataAdapter mBatteryStatsDataAdapter; - private Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh; + private final Runnable mBatteryStatsRefresh = this::periodicBatteryStatsRefresh; private SharedPreferences mSharedPref; private String mBatteryConsumerId; private TextView mTitleView; private TextView mDetailsView; private ImageView mIconView; private TextView mPackagesView; + private View mHeadingsView; private RecyclerView mBatteryConsumerDataView; private View mLoadingView; private View mEmptyView; - private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult( + private final ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult( BatteryConsumerPickerActivity.CONTRACT, this::onApplicationSelected); - private BatteryStatsHelper mBatteryStatsHelper; private List<BatteryUsageStats> mBatteryUsageStats; @Override @@ -85,6 +81,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity { mDetailsView = findViewById(R.id.details); mIconView = findViewById(android.R.id.icon); mPackagesView = findViewById(R.id.packages); + mHeadingsView = findViewById(R.id.headings); mBatteryConsumerDataView = findViewById(R.id.battery_consumer_data_view); mBatteryConsumerDataView.setLayoutManager(new LinearLayoutManager(this)); @@ -139,55 +136,10 @@ public class BatteryStatsViewerActivity extends ComponentActivity { private void loadBatteryStats() { LoaderManager loaderManager = LoaderManager.getInstance(this); - loaderManager.restartLoader(LOADER_BATTERY_STATS_HELPER, null, - new BatteryStatsHelperLoaderCallbacks()); loaderManager.restartLoader(LOADER_BATTERY_USAGE_STATS, null, new BatteryUsageStatsLoaderCallbacks()); } - private static class BatteryStatsHelperLoader extends AsyncLoaderCompat<BatteryStatsHelper> { - private final BatteryStatsHelper mBatteryStatsHelper; - private final UserManager mUserManager; - - BatteryStatsHelperLoader(Context context) { - super(context); - mUserManager = context.getSystemService(UserManager.class); - mBatteryStatsHelper = new BatteryStatsHelper(context, - false /* collectBatteryBroadcast */); - mBatteryStatsHelper.create((Bundle) null); - mBatteryStatsHelper.clearStats(); - } - - @Override - public BatteryStatsHelper loadInBackground() { - mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, - UserHandle.myUserId()); - return mBatteryStatsHelper; - } - - @Override - protected void onDiscardResult(BatteryStatsHelper result) { - } - } - - private class BatteryStatsHelperLoaderCallbacks implements LoaderCallbacks<BatteryStatsHelper> { - @NonNull - @Override - public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) { - return new BatteryStatsHelperLoader(BatteryStatsViewerActivity.this); - } - - @Override - public void onLoadFinished(@NonNull Loader<BatteryStatsHelper> loader, - BatteryStatsHelper batteryStatsHelper) { - onBatteryStatsHelperLoaded(batteryStatsHelper); - } - - @Override - public void onLoaderReset(@NonNull Loader<BatteryStatsHelper> loader) { - } - } - private static class BatteryUsageStatsLoader extends AsyncLoaderCompat<List<BatteryUsageStats>> { private final BatteryStatsManager mBatteryStatsManager; @@ -200,10 +152,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity { @Override public List<BatteryUsageStats> loadInBackground() { final BatteryUsageStatsQuery queryDefault = - new BatteryUsageStatsQuery.Builder().build(); + new BatteryUsageStatsQuery.Builder() + .includePowerModels() + .build(); final BatteryUsageStatsQuery queryPowerProfileModeledOnly = new BatteryUsageStatsQuery.Builder() .powerProfileModeledOnly() + .includePowerModels() .build(); return mBatteryStatsManager.getBatteryUsageStats( List.of(queryDefault, queryPowerProfileModeledOnly)); @@ -233,22 +188,13 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } } - public void onBatteryStatsHelperLoaded(BatteryStatsHelper batteryStatsHelper) { - mBatteryStatsHelper = batteryStatsHelper; - onBatteryStatsDataLoaded(); - } - private void onBatteryUsageStatsLoaded(List<BatteryUsageStats> batteryUsageStats) { mBatteryUsageStats = batteryUsageStats; onBatteryStatsDataLoaded(); } public void onBatteryStatsDataLoaded() { - if (mBatteryStatsHelper == null || mBatteryUsageStats == null) { - return; - } - - BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this, mBatteryStatsHelper, + BatteryConsumerData batteryConsumerData = new BatteryConsumerData(this, mBatteryUsageStats, mBatteryConsumerId); BatteryConsumerInfoHelper.BatteryConsumerInfo @@ -256,6 +202,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity { if (batteryConsumerInfo == null) { mTitleView.setText("Battery consumer not found"); mPackagesView.setVisibility(View.GONE); + mHeadingsView.setVisibility(View.GONE); } else { mTitleView.setText(batteryConsumerInfo.label); if (batteryConsumerInfo.details != null) { @@ -273,6 +220,12 @@ public class BatteryStatsViewerActivity extends ComponentActivity { } else { mPackagesView.setVisibility(View.GONE); } + + if (batteryConsumerInfo.isSystemBatteryConsumer) { + mHeadingsView.setVisibility(View.VISIBLE); + } else { + mHeadingsView.setVisibility(View.GONE); + } } mBatteryStatsDataAdapter.setEntries(batteryConsumerData.getEntries()); @@ -290,6 +243,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity { private static class BatteryStatsDataAdapter extends RecyclerView.Adapter<BatteryStatsDataAdapter.ViewHolder> { public static class ViewHolder extends RecyclerView.ViewHolder { + public ImageView iconImageView; public TextView titleTextView; public TextView amountTextView; public TextView percentTextView; @@ -297,6 +251,7 @@ public class BatteryStatsViewerActivity extends ComponentActivity { ViewHolder(View itemView) { super(itemView); + iconImageView = itemView.findViewById(R.id.icon); titleTextView = itemView.findViewById(R.id.title); amountTextView = itemView.findViewById(R.id.amount); percentTextView = itemView.findViewById(R.id.percent); @@ -328,21 +283,56 @@ public class BatteryStatsViewerActivity extends ComponentActivity { public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) { BatteryConsumerData.Entry entry = mEntries.get(position); switch (entry.entryType) { - case POWER: + case POWER_MODELED: viewHolder.titleTextView.setText(entry.title); viewHolder.amountTextView.setText( String.format(Locale.getDefault(), "%.1f mAh", entry.value)); + viewHolder.iconImageView.setImageResource(R.drawable.gm_calculate_24); + viewHolder.itemView.setBackgroundResource( + R.color.battery_consumer_bg_power_profile); break; - case DURATION: + case POWER_MEASURED: viewHolder.titleTextView.setText(entry.title); viewHolder.amountTextView.setText( - String.format(Locale.getDefault(), "%,d ms", (long) entry.value)); + String.format(Locale.getDefault(), "%.1f mAh", entry.value)); + viewHolder.iconImageView.setImageResource(R.drawable.gm_amp_24); + viewHolder.itemView.setBackgroundResource( + R.color.battery_consumer_bg_measured_energy); + break; + case POWER_CUSTOM: + viewHolder.titleTextView.setText(entry.title); + viewHolder.amountTextView.setText( + String.format(Locale.getDefault(), "%.1f mAh", entry.value)); + viewHolder.iconImageView.setImageResource(R.drawable.gm_custom_24); + viewHolder.itemView.setBackgroundResource( + R.color.battery_consumer_bg_measured_energy); + break; + case DURATION: + viewHolder.titleTextView.setText(entry.title); + final long durationMs = (long) entry.value; + CharSequence text; + if (durationMs < MILLIS_IN_MINUTE) { + text = String.format(Locale.getDefault(), "%,d ms", durationMs); + } else { + text = String.format(Locale.getDefault(), "%,d m %d s", + durationMs / MILLIS_IN_MINUTE, + (durationMs % MILLIS_IN_MINUTE) / 1000); + } + + viewHolder.amountTextView.setText(text); + viewHolder.iconImageView.setImageResource(R.drawable.gm_timer_24); + viewHolder.itemView.setBackground(null); break; } - double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0; - viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%", - proportion)); + double proportion; + if (entry.isSystemBatteryConsumer) { + proportion = entry.value != 0 ? entry.total * 100 / entry.value : 0; + } else { + proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0; + } + viewHolder.percentTextView.setText( + String.format(Locale.getDefault(), "%.1f%%", proportion)); } } } diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index e5da41c7c113..2e2e6bd07539 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -86,7 +86,6 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @MediumTest @Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") public class ActivityThreadTest { private static final int TIMEOUT_SEC = 10; @@ -352,8 +351,9 @@ public class ActivityThreadTest { final Rect bounds = activity.getWindowManager().getCurrentWindowMetrics().getBounds(); assertEquals(activityConfigPortrait.windowConfiguration.getBounds(), bounds); - // Ensure that Activity#onConfigurationChanged() is only called once. - assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges); + // Ensure that Activity#onConfigurationChanged() not be called because the changes in + // WindowConfiguration shouldn't be reported. + assertEquals(numOfConfig, activity.mNumOfConfigChanges); } @Test @@ -379,7 +379,7 @@ public class ActivityThreadTest { Configuration config = new Configuration(); config.seq = BASE_SEQ + 1; - config.smallestScreenWidthDp = 100; + config.orientation = ORIENTATION_LANDSCAPE; appThread.scheduleTransaction(newActivityConfigTransaction(activity, config)); // Wait until the main thread is performing the configuration change for the configuration @@ -389,17 +389,17 @@ public class ActivityThreadTest { config = new Configuration(); config.seq = BASE_SEQ + 2; - config.smallestScreenWidthDp = 200; + config.orientation = ORIENTATION_PORTRAIT; appThread.scheduleTransaction(newActivityConfigTransaction(activity, config)); config = new Configuration(); config.seq = BASE_SEQ + 3; - config.smallestScreenWidthDp = 300; + config.orientation = ORIENTATION_LANDSCAPE; appThread.scheduleTransaction(newActivityConfigTransaction(activity, config)); config = new Configuration(); config.seq = BASE_SEQ + 4; - config.smallestScreenWidthDp = 400; + config.orientation = ORIENTATION_PORTRAIT; appThread.scheduleTransaction(newActivityConfigTransaction(activity, config)); activity.mConfigLatch.countDown(); @@ -411,7 +411,7 @@ public class ActivityThreadTest { // Only two more configuration changes: one with seq BASE_SEQ + 1; another with seq // BASE_SEQ + 4. Configurations scheduled in between should be dropped. assertEquals(numOfConfig + 2, activity.mNumOfConfigChanges); - assertEquals(400, activity.mConfig.smallestScreenWidthDp); + assertEquals(ORIENTATION_PORTRAIT, activity.mConfig.orientation); } @Test @@ -515,6 +515,7 @@ public class ActivityThreadTest { } @Test + @FlakyTest(bugId = 176134235) public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() { final TestActivity activity = mActivityTestRule.launchActivity(new Intent()); diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java index 614e7c1d6fa4..83280f18c889 100644 --- a/core/tests/coretests/src/android/window/WindowContextTest.java +++ b/core/tests/coretests/src/android/window/WindowContextTest.java @@ -17,6 +17,7 @@ package android.window; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -209,6 +210,38 @@ public class WindowContextTest { mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY); } + @Test + public void testWindowContextAddViewWithSubWindowType_NotCrash() throws Throwable { + final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD); + final WindowManager wm = windowContext.getSystemService(WindowManager.class); + + // Create a WindowToken with system window type. + final IBinder existingToken = new Binder(); + mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(), + null /* options */); + + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams(TYPE_INPUT_METHOD); + params.token = existingToken; + final View parentWindow = new View(windowContext); + + final AttachStateListener listener = new AttachStateListener(); + parentWindow.addOnAttachStateChangeListener(listener); + + // Add the parent window + mInstrumentation.runOnMainSync(() -> wm.addView(parentWindow, params)); + + assertTrue(listener.mLatch.await(4, TimeUnit.SECONDS)); + + final WindowManager.LayoutParams subWindowAttrs = + new WindowManager.LayoutParams(TYPE_APPLICATION_ATTACHED_DIALOG); + subWindowAttrs.token = parentWindow.getWindowToken(); + final View subWindow = new View(windowContext); + + // Add a window with sub-window type. + mInstrumentation.runOnMainSync(() -> wm.addView(subWindow, subWindowAttrs)); + } + private WindowContext createWindowContext() { return createWindowContext(TYPE_APPLICATION_OVERLAY); } @@ -219,4 +252,16 @@ public class WindowContextTest { .getDisplay(DEFAULT_DISPLAY); return (WindowContext) instContext.createWindowContext(display, type, null /* options */); } + + private static class AttachStateListener implements View.OnAttachStateChangeListener { + final CountDownLatch mLatch = new CountDownLatch(1); + + @Override + public void onViewAttachedToWindow(View v) { + mLatch.countDown(); + } + + @Override + public void onViewDetachedFromWindow(View v) {} + } } diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java index cf47efddddd9..c63ec45ed5a9 100644 --- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java @@ -65,12 +65,12 @@ public class AmbientDisplayPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) .isEqualTo(90 * MINUTE_IN_MS); // 100,000,00 uC / 1000 (micro-/milli-) / 360 (seconds/hour) = 27.777778 mAh - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(27.777778); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); } @@ -91,11 +91,11 @@ public class AmbientDisplayPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer( SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) .isEqualTo(90 * MINUTE_IN_MS); - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(15.0); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java index 1e614c480bde..3a6f7b8a1618 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -109,11 +109,11 @@ public class BatteryUsageStatsRule implements TestRule { } /** Call only after setting the power profile information. */ - public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(int numCustom) { + public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() { final boolean[] supportedStandardBuckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; Arrays.fill(supportedStandardBuckets, true); - mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, numCustom); + mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets, new String[0]); mBatteryStats.informThatAllExternalStatsAreFlushed(); return this; } @@ -167,15 +167,11 @@ public class BatteryUsageStatsRule implements TestRule { } BatteryUsageStats apply(BatteryUsageStatsQuery query, PowerCalculator... calculators) { - final long[] customMeasuredEnergiesMicroJoules = - mBatteryStats.getCustomConsumerMeasuredBatteryConsumptionUC(); - final int customMeasuredEnergiesCount = customMeasuredEnergiesMicroJoules != null - ? customMeasuredEnergiesMicroJoules.length - : 0; + final String[] customPowerComponentNames = mBatteryStats.getCustomPowerComponentNames(); final boolean includePowerModels = (query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0; BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder( - customMeasuredEnergiesCount, 0, includePowerModels); + customPowerComponentNames, 0, includePowerModels); SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats(); for (int i = 0; i < uidStats.size(); i++) { builder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 60df9688b5a4..b25359970c5a 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -66,17 +66,18 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); - final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1) - .setDischargePercentage(20) - .setDischargedPowerRange(1000, 2000) - .setStatsStartTimestamp(1000); + final BatteryUsageStats.Builder builder = + new BatteryUsageStats.Builder(new String[]{"FOO"}, 1) + .setDischargePercentage(20) + .setDischargedPowerRange(1000, 2000) + .setStatsStartTimestamp(1000); builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) .setPackageWithHighestDrain("foo") .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000) .setConsumedPower( - BatteryConsumer.POWER_COMPONENT_USAGE, 300) + BatteryConsumer.POWER_COMPONENT_SCREEN, 300) .setConsumedPower( BatteryConsumer.POWER_COMPONENT_CPU, 400) .setConsumedPowerForCustomComponent( @@ -122,7 +123,7 @@ public class BatteryUsageStatsTest { assertThat(uidBatteryConsumer.getTimeInStateMs( UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000); assertThat(uidBatteryConsumer.getConsumedPower( - BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300); + BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(300); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400); assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent( @@ -134,6 +135,9 @@ public class BatteryUsageStatsTest { assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis( BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800); assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200); + assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); + assertThat(uidBatteryConsumer.getCustomPowerComponentName( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); } else { fail("Unexpected UID " + uidBatteryConsumer.getUid()); } @@ -153,6 +157,11 @@ public class BatteryUsageStatsTest { BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400); assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(20300); assertThat(systemBatteryConsumer.getPowerConsumedByApps()).isEqualTo(20000); + assertThat(systemBatteryConsumer.getUsageDurationMillis()) + .isEqualTo(10400); // max + assertThat(systemBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1); + assertThat(systemBatteryConsumer.getCustomPowerComponentName( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO"); } else { fail("Unexpected drain type " + systemBatteryConsumer.getDrainType()); } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java index f65fb9583d6c..bf87683593e7 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java @@ -16,6 +16,8 @@ package com.android.internal.os; +import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -23,16 +25,21 @@ import static org.junit.Assert.assertEquals; import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; +import android.util.proto.ProtoOutputStream; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BinderInternal.CallSession; import com.android.internal.os.BinderLatencyObserver.LatencyDims; +import com.android.internal.os.BinderLatencyProto.ApiStats; +import com.android.internal.os.BinderLatencyProto.Dims; +import com.android.internal.os.BinderLatencyProto.RepeatedApiStats; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.Arrays; import java.util.Random; @@ -49,11 +56,17 @@ public class BinderLatencyObserverTest { CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + + blo.setElapsedTime(2); blo.callEnded(callSession); + blo.setElapsedTime(4); blo.callEnded(callSession); + blo.setElapsedTime(6); blo.callEnded(callSession); callSession.transactionCode = 2; + blo.setElapsedTime(8); blo.callEnded(callSession); + blo.setElapsedTime(10); blo.callEnded(callSession); ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms(); @@ -74,8 +87,10 @@ public class BinderLatencyObserverTest { CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + blo.setElapsedTime(2); blo.callEnded(callSession); callSession.transactionCode = 2; + blo.setElapsedTime(4); blo.callEnded(callSession); ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms(); @@ -89,13 +104,13 @@ public class BinderLatencyObserverTest { @Test public void testTooCallLengthOverflow() { TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); - blo.setElapsedTime(2L + (long) Integer.MAX_VALUE); blo.setHistogramBucketsParams(5, 5, 1.125f); Binder binder = new Binder(); CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + blo.setElapsedTime(2L + (long) Integer.MAX_VALUE); blo.callEnded(callSession); // The long call should be capped to maxint (to not overflow) and placed in the last bucket. @@ -114,6 +129,7 @@ public class BinderLatencyObserverTest { CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + blo.setElapsedTime(2); blo.callEnded(callSession); LatencyDims dims = new LatencyDims(binder.getClass(), 1); @@ -122,14 +138,111 @@ public class BinderLatencyObserverTest { assertThat(blo.getLatencyHistograms().get(dims)) .asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); // Try to add another sample. + blo.setElapsedTime(2); blo.callEnded(callSession); // Make sure the buckets don't overflow. assertThat(blo.getLatencyHistograms().get(dims)) .asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); } + @Test + public void testSingleAtomPush() { + TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); + + Binder binder = new Binder(); + CallSession callSession = new CallSession(); + callSession.binderClass = binder.getClass(); + callSession.transactionCode = 1; + blo.setElapsedTime(7); + blo.callEnded(callSession); + blo.callEnded(callSession); + blo.setElapsedTime(8); + blo.callEnded(callSession); + + // Trigger the statsd push. + blo.getStatsdPushRunnable().run(); + + ProtoOutputStream expectedProto = new ProtoOutputStream(); + long apiStatsToken = expectedProto.start(RepeatedApiStats.API_STATS); + long dimsToken = expectedProto.start(ApiStats.DIMS); + expectedProto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto.write(Dims.SERVICE_METHOD_NAME, "1"); + expectedProto.end(dimsToken); + expectedProto.write(ApiStats.FIRST_BUCKET_INDEX, 3); + expectedProto.write(ApiStats.BUCKETS, 2); + expectedProto.write(ApiStats.BUCKETS, 1); + expectedProto.end(apiStatsToken); + + assertThat(blo.getWrittenAtoms()) + .containsExactly(Arrays.toString(expectedProto.getBytes())); + } + + @Test + public void testMultipleAtomPush() { + TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); + + BinderTransactionNameResolver resolver = new BinderTransactionNameResolver(); + + + Binder binder = new Binder(); + CallSession callSession = new CallSession(); + callSession.binderClass = binder.getClass(); + callSession.transactionCode = 1; + blo.setElapsedTime(1); + blo.callEnded(callSession); + callSession.transactionCode = 2; + blo.setElapsedTime(5); + blo.callEnded(callSession); + callSession.transactionCode = 3; + blo.callEnded(callSession); + + // Trigger the statsd push. + blo.getStatsdPushRunnable().run(); + + ProtoOutputStream expectedProto1 = new ProtoOutputStream(); + long apiStatsToken = expectedProto1.start(RepeatedApiStats.API_STATS); + long dimsToken = expectedProto1.start(ApiStats.DIMS); + expectedProto1.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto1.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto1.write(Dims.SERVICE_METHOD_NAME, "1"); + expectedProto1.end(dimsToken); + expectedProto1.write(ApiStats.FIRST_BUCKET_INDEX, 0); + expectedProto1.write(ApiStats.BUCKETS, 1); + expectedProto1.end(apiStatsToken); + + apiStatsToken = expectedProto1.start(RepeatedApiStats.API_STATS); + dimsToken = expectedProto1.start(ApiStats.DIMS); + expectedProto1.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto1.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto1.write(Dims.SERVICE_METHOD_NAME, "2"); + expectedProto1.end(dimsToken); + expectedProto1.write(ApiStats.FIRST_BUCKET_INDEX, 1); + expectedProto1.write(ApiStats.BUCKETS, 1); + expectedProto1.end(apiStatsToken); + + ProtoOutputStream expectedProto2 = new ProtoOutputStream(); + apiStatsToken = expectedProto2.start(RepeatedApiStats.API_STATS); + dimsToken = expectedProto2.start(ApiStats.DIMS); + expectedProto2.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto2.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto2.write(Dims.SERVICE_METHOD_NAME, "3"); + expectedProto2.end(dimsToken); + expectedProto2.write(ApiStats.FIRST_BUCKET_INDEX, 1); + expectedProto2.write(ApiStats.BUCKETS, 1); + expectedProto2.end(apiStatsToken); + + // Each ApiStats is around ~60 bytes so only two should fit in an atom. + assertThat(blo.getWrittenAtoms()) + .containsExactly( + Arrays.toString(expectedProto1.getBytes()), + Arrays.toString(expectedProto2.getBytes())) + .inOrder(); + } + public static class TestBinderLatencyObserver extends BinderLatencyObserver { private long mElapsedTime = 0; + private ArrayList<String> mWrittenAtoms; TestBinderLatencyObserver() { // Make random generator not random. @@ -145,16 +258,30 @@ public class BinderLatencyObserverTest { } }); setSamplingInterval(1); + mWrittenAtoms = new ArrayList<>(); } @Override protected long getElapsedRealtimeMicro() { - mElapsedTime += 2; return mElapsedTime; } + @Override + protected int getMaxAtomSizeBytes() { + return 1100; + } + + @Override + protected void writeAtomToStatsd(ProtoOutputStream atom) { + mWrittenAtoms.add(Arrays.toString(atom.getBytes())); + } + public void setElapsedTime(long time) { mElapsedTime = time; } + + public ArrayList<String> getWrittenAtoms() { + return mWrittenAtoms; + } } } diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java index 1a87c1084fe0..71cdb5fe17f0 100644 --- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java @@ -44,7 +44,7 @@ public class BluetoothPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0) .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0) .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0) - .initMeasuredEnergyStatsLocked(0); + .initMeasuredEnergyStatsLocked(); @Test public void testTimerBasedModel() { diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java index 31abbc20a090..63af21dcdd93 100644 --- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java @@ -96,7 +96,7 @@ public class CpuPowerCalculatorTest { .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader) .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader) .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) - .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0); + .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]); } @Test diff --git a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java index 95c3b4ed8a40..aa066c36cd4b 100644 --- a/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/GnssPowerCalculatorTest.java @@ -42,7 +42,7 @@ public class GnssPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_GPS_ON, 360.0) .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[] {720.0, 1440.0, 1800.0}) - .initMeasuredEnergyStatsLocked(0); + .initMeasuredEnergyStatsLocked(); @Test public void testTimerBasedModel() { diff --git a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java index 781e72560279..a9800b7e6b75 100644 --- a/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/IdlePowerCalculatorTest.java @@ -48,9 +48,9 @@ public class IdlePowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_IDLE); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_IDLE)) .isEqualTo(3000); - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_IDLE)) .isWithin(PRECISION).of(0.7); } } diff --git a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java index 8f21503a6d77..71dbcdbf7f46 100644 --- a/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MemoryPowerCalculatorTest.java @@ -55,9 +55,9 @@ public class MemoryPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MEMORY); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MEMORY)) .isEqualTo(3000); - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MEMORY)) .isWithin(PRECISION).of(0.7); } } diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java index 3505e8c34027..5b84a1b320c7 100644 --- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java @@ -57,7 +57,7 @@ public class MobileRadioPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0) .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0}) - .initMeasuredEnergyStatsLocked(0); + .initMeasuredEnergyStatsLocked(); @Test public void testCounterBasedModel() { diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 26adbe9e7c59..7a7d9f541328 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -51,7 +51,9 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { final boolean[] supportedStandardBuckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; Arrays.fill(supportedStandardBuckets, true); - mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedStandardBuckets, 2); + final String[] customBucketNames = {"FOO", "BAR"}; + mGlobalMeasuredEnergyStats = + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); // A no-op handler. mHandler = new Handler(Looper.getMainLooper()) { diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java index 9cd6ea8a6a3b..7d829e44d9ec 100644 --- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java @@ -84,13 +84,13 @@ public class ScreenPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); // 600000000 uAs * (1 mA / 1000 uA) * (1 h / 3600 s) = 166.66666 mAh - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(166.66666); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY); assertThat(consumer.getConsumedPower()) .isWithin(PRECISION).of(166.66666); @@ -153,11 +153,11 @@ public class ScreenPowerCalculatorTest { SystemBatteryConsumer consumer = mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_SCREEN); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE)) + assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SCREEN)) .isEqualTo(80 * MINUTE_IN_MS); - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isWithin(PRECISION).of(92.0); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_USAGE)) + assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN)) .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); assertThat(consumer.getConsumedPower()) .isWithin(PRECISION).of(92.0); diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java index 2e501dbe355e..9349bce2c383 100644 --- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -53,7 +53,7 @@ public class WifiPowerCalculatorTest { .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0) .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0) .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0) - .initMeasuredEnergyStatsLocked(0); + .initMeasuredEnergyStatsLocked(); /** Sets up a batterystats object with pre-populated network values. */ private BatteryStatsImpl setupTestNetworkNumbers() { diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java index ed6e27b199b9..f1edc871a56c 100644 --- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -23,6 +23,8 @@ import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON; import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -49,13 +51,13 @@ public class MeasuredEnergyStatsTest { @Test public void testConstruction() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) { if (supportedStandardBuckets[i]) { @@ -66,21 +68,22 @@ public class MeasuredEnergyStatsTest { assertEquals(POWER_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketCharge(i)); } } - for (int i = 0; i < numCustomBuckets; i++) { + for (int i = 0; i < customBucketNames.length; i++) { assertEquals(0L, stats.getAccumulatedCustomBucketCharge(i)); } + assertThat(stats.getCustomBucketNames()).asList().containsExactly("A", "B"); } @Test public void testCreateFromTemplate() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -99,7 +102,7 @@ public class MeasuredEnergyStatsTest { newStats.getAccumulatedStandardBucketCharge(i)); } } - for (int i = 0; i < numCustomBuckets; i++) { + for (int i = 0; i < customBucketNames.length; i++) { assertEquals(0L, newStats.getAccumulatedCustomBucketCharge(i)); } } @@ -107,13 +110,13 @@ public class MeasuredEnergyStatsTest { @Test public void testReadWriteParcel() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -130,25 +133,25 @@ public class MeasuredEnergyStatsTest { assertEquals(stats.getAccumulatedStandardBucketCharge(i), newStats.getAccumulatedStandardBucketCharge(i)); } - for (int i = 0; i < numCustomBuckets; i++) { + for (int i = 0; i < customBucketNames.length; i++) { assertEquals(stats.getAccumulatedCustomBucketCharge(i), newStats.getAccumulatedCustomBucketCharge(i)); } assertEquals(POWER_DATA_UNAVAILABLE, - newStats.getAccumulatedCustomBucketCharge(numCustomBuckets + 1)); + newStats.getAccumulatedCustomBucketCharge(customBucketNames.length + 1)); parcel.recycle(); } @Test public void testCreateAndReadSummaryFromParcel() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -166,25 +169,25 @@ public class MeasuredEnergyStatsTest { assertEquals(stats.getAccumulatedStandardBucketCharge(i), newStats.getAccumulatedStandardBucketCharge(i)); } - for (int i = 0; i < numCustomBuckets; i++) { + for (int i = 0; i < customBucketNames.length; i++) { assertEquals(stats.getAccumulatedCustomBucketCharge(i), newStats.getAccumulatedCustomBucketCharge(i)); } assertEquals(POWER_DATA_UNAVAILABLE, - newStats.getAccumulatedCustomBucketCharge(numCustomBuckets + 1)); + newStats.getAccumulatedCustomBucketCharge(customBucketNames.length + 1)); parcel.recycle(); } @Test public void testCreateAndReadSummaryFromParcel_existingTemplate() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats template = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); template.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -205,7 +208,7 @@ public class MeasuredEnergyStatsTest { newsupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true newsupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false final MeasuredEnergyStats newTemplate = - new MeasuredEnergyStats(newsupportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(newsupportedStandardBuckets, customBucketNames); parcel.setDataPosition(0); final MeasuredEnergyStats newStats = @@ -225,23 +228,23 @@ public class MeasuredEnergyStatsTest { newStats.getAccumulatedStandardBucketCharge(i)); } } - for (int i = 0; i < numCustomBuckets; i++) { + for (int i = 0; i < customBucketNames.length; i++) { assertEquals(stats.getAccumulatedCustomBucketCharge(i), newStats.getAccumulatedCustomBucketCharge(i)); } assertEquals(POWER_DATA_UNAVAILABLE, - newStats.getAccumulatedCustomBucketCharge(numCustomBuckets + 1)); + newStats.getAccumulatedCustomBucketCharge(customBucketNames.length + 1)); parcel.recycle(); } @Test public void testCreateAndReadSummaryFromParcel_skipZero() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; Arrays.fill(supportedStandardBuckets, true); final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); // Accumulate charge in one bucket and one custom bucket, the rest should be zero stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 200); stats.updateCustomBucket(1, 60); @@ -298,13 +301,13 @@ public class MeasuredEnergyStatsTest { @Test public void testCreateAndReadSummaryFromParcel_nullTemplate() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -324,13 +327,13 @@ public class MeasuredEnergyStatsTest { @Test public void testCreateAndReadSummaryFromParcel_boring() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats template = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); template.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); template.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -348,7 +351,7 @@ public class MeasuredEnergyStatsTest { newSupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true newSupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false final MeasuredEnergyStats newTemplate = - new MeasuredEnergyStats(newSupportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(newSupportedStandardBuckets, customBucketNames); parcel.setDataPosition(0); final MeasuredEnergyStats newStats = @@ -362,13 +365,13 @@ public class MeasuredEnergyStatsTest { @Test public void testUpdateBucket() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); stats.updateStandardBucket(POWER_BUCKET_SCREEN_DOZE, 30); stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -389,7 +392,8 @@ public class MeasuredEnergyStatsTest { @Test public void testIsValidCustomBucket() { final MeasuredEnergyStats stats = - new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], 3); + new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], + new String[]{"A", "B", "C"}); assertFalse(stats.isValidCustomBucket(-1)); assertTrue(stats.isValidCustomBucket(0)); assertTrue(stats.isValidCustomBucket(1)); @@ -398,7 +402,7 @@ public class MeasuredEnergyStatsTest { assertFalse(stats.isValidCustomBucket(4)); final MeasuredEnergyStats boringStats = - new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], 0); + new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[0]); assertFalse(boringStats.isValidCustomBucket(-1)); assertFalse(boringStats.isValidCustomBucket(0)); assertFalse(boringStats.isValidCustomBucket(1)); @@ -407,8 +411,8 @@ public class MeasuredEnergyStatsTest { @Test public void testGetAccumulatedCustomBucketCharges() { final MeasuredEnergyStats stats = - new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], 3); - + new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], + new String[]{"A", "B", "C"}); stats.updateCustomBucket(0, 50); stats.updateCustomBucket(1, 60); stats.updateCustomBucket(2, 13); @@ -425,7 +429,7 @@ public class MeasuredEnergyStatsTest { @Test public void testGetAccumulatedCustomBucketCharges_empty() { final MeasuredEnergyStats stats = - new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], 0); + new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[0]); final long[] output = stats.getAccumulatedCustomBucketCharges(); assertEquals(0, output.length); @@ -433,22 +437,23 @@ public class MeasuredEnergyStatsTest { @Test public void testGetNumberCustomChargeBuckets() { - assertEquals(0, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], 0) - .getNumberCustomPowerBuckets()); - assertEquals(3, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], 3) - .getNumberCustomPowerBuckets()); + assertEquals(0, + new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], new String[0]) + .getNumberCustomPowerBuckets()); + assertEquals(3, new MeasuredEnergyStats(new boolean[NUMBER_STANDARD_POWER_BUCKETS], + new String[]{"A", "B", "C"}).getNumberCustomPowerBuckets()); } @Test public void testReset() { final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS]; - final int numCustomBuckets = 2; + final String[] customBucketNames = {"A", "B"}; supportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true; supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false; supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true; final MeasuredEnergyStats stats = - new MeasuredEnergyStats(supportedStandardBuckets, numCustomBuckets); + new MeasuredEnergyStats(supportedStandardBuckets, customBucketNames); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10); stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5); stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40); @@ -466,7 +471,7 @@ public class MeasuredEnergyStatsTest { assertEquals(POWER_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketCharge(i)); } } - for (int i = 0; i < numCustomBuckets; i++) { + for (int i = 0; i < customBucketNames.length; i++) { assertEquals(0, stats.getAccumulatedCustomBucketCharge(i)); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 2fe8b285ed88..5f344267d71c 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -348,6 +348,8 @@ applications that come with the platform <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/> <permission name="android.permission.INSTALL_PACKAGES"/> <!-- Needed for test only --> + <permission name="android.permission.ACCESS_MTP"/> + <!-- Needed for test only --> <permission name="android.permission.INTERACT_ACROSS_PROFILES"/> <!-- Permission required to test onPermissionsChangedListener --> <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/> @@ -404,6 +406,7 @@ applications that come with the platform <permission name="android.permission.TOGGLE_AUTOMOTIVE_PROJECTION" /> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.UWB_PRIVILEGED"/> <permission name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"/> <permission name="android.permission.WRITE_MEDIA_STORAGE"/> <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index fec78f070311..81f1021c6eab 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1825,6 +1825,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/InsetsSourceProvider.java" }, + "35398067": { + "message": "goodToGo(): onAnimationStart, transit=%s, apps=%d, wallpapers=%d, nonApps=%d", + "level": "DEBUG", + "group": "WM_DEBUG_REMOTE_ANIMATIONS", + "at": "com\/android\/server\/wm\/RemoteAnimationController.java" + }, "38267433": { "message": "Attempted to reset replacing window on non-existing app token %s", "level": "WARN", diff --git a/libs/WindowManager/Shell/res/drawable/pip_menu_background.xml b/libs/WindowManager/Shell/res/drawable/pip_menu_background.xml new file mode 100644 index 000000000000..29907a61b4cc --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/pip_menu_background.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#FF000000"/> + + <corners android:radius="@dimen/pip_corner_radius" /> +</shape>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 11c146457844..dca598518432 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -739,14 +739,11 @@ public class BubbleController { return (isSummary && isSuppressedSummary) || isSuppressedBubble; } - private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback, - Executor callbackExecutor) { + private void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback) { if (mBubbleData.isSummarySuppressed(groupKey)) { mBubbleData.removeSuppressedSummary(groupKey); if (callback != null) { - callbackExecutor.execute(() -> { - callback.accept(mBubbleData.getSummaryKey(groupKey)); - }); + callback.accept(mBubbleData.getSummaryKey(groupKey)); } } } @@ -1298,8 +1295,10 @@ public class BubbleController { public void removeSuppressedSummaryIfNecessary(String groupKey, Consumer<String> callback, Executor callbackExecutor) { mMainExecutor.execute(() -> { - BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, callback, - callbackExecutor); + Consumer<String> cb = callback != null + ? (key) -> callbackExecutor.execute(() -> callback.accept(key)) + : null; + BubbleController.this.removeSuppressedSummaryIfNecessary(groupKey, cb); }); } @@ -1340,10 +1339,13 @@ public class BubbleController { @Override public boolean handleDismissalInterception(BubbleEntry entry, - @Nullable List<BubbleEntry> children, IntConsumer removeCallback) { + @Nullable List<BubbleEntry> children, IntConsumer removeCallback, + Executor callbackExecutor) { + IntConsumer cb = removeCallback != null + ? (index) -> callbackExecutor.execute(() -> removeCallback.accept(index)) + : null; return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.handleDismissalInterception(entry, children, - removeCallback); + return BubbleController.this.handleDismissalInterception(entry, children, cb); }, Boolean.class); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 9fc8aef47064..1bfb61929297 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -140,7 +140,7 @@ public interface Bubbles { * @return true if we want to intercept the dismissal of the entry, else false. */ boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children, - IntConsumer removeCallback); + IntConsumer removeCallback, Executor callbackExecutor); /** Set the proxy to commnuicate with SysUi side components. */ void setSysuiProxy(SysuiProxy proxy); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 5c3af3ee5330..8ac9a7a479db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -37,6 +37,7 @@ import com.android.wm.shell.animation.Interpolators; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Controller class of PiP animations (both from and to PiP mode). @@ -112,6 +113,7 @@ public class PipAnimationController { PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart, alphaEnd)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA + && Objects.equals(destinationBounds, mCurrentAnimator.getDestinationBounds()) && mCurrentAnimator.isRunning()) { mCurrentAnimator.updateEndValue(alphaEnd); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java index 3af0ff0dfb36..d4f229cb3e09 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java @@ -216,7 +216,7 @@ public class PipMediaController { } ArrayList<RemoteAction> mediaActions = new ArrayList<>(); - boolean isPlaying = mMediaController.getPlaybackState().isActiveState(); + boolean isPlaying = mMediaController.getPlaybackState().isActive(); long actions = mMediaController.getPlaybackState().getActions(); // Prev action diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index e152633063be..e66be66c8ef4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -99,9 +99,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private enum State { UNDEFINED(0), TASK_APPEARED(1), - ENTERING_PIP(2), - ENTERED_PIP(3), - EXITING_PIP(4); + ENTRY_SCHEDULED(2), + ENTERING_PIP(3), + ENTERED_PIP(4), + EXITING_PIP(5); private final int mStateValue; @@ -265,6 +266,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** + * Returns whether the entry animation is waiting to be started. + */ + public boolean isEntryScheduled() { + return mState == State.ENTRY_SCHEDULED; + } + + /** * Registers a callback when a display change has been detected when we enter PiP. */ public void registerOnDisplayIdChangeCallback(IntConsumer onDisplayIdChangeCallback) { @@ -492,6 +500,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + /** + * Called when the display rotation handling is skipped (e.g. when rotation happens while in + * the middle of an entry transition). + */ + public void onDisplayRotationSkipped() { + if (isEntryScheduled()) { + // The PIP animation is scheduled to start with the previous orientation's bounds, + // re-calculate the entry bounds and restart the alpha animation. + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); + } + } + @VisibleForTesting void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) { // If we are fading the PIP in, then we should move the pip to the final location as @@ -501,6 +522,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); + mState = State.ENTRY_SCHEDULED; applyEnterPipSyncTransaction(destinationBounds, () -> { mPipAnimationController .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 62ae1d5d600c..f505e60de61e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -116,7 +116,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { if (!mPipTaskOrganizer.isInPip() || mPipBoundsState.getDisplayLayout().rotation() == toRotation - || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { + || mPipTaskOrganizer.isDeferringEnterPipAnimation() + || mPipTaskOrganizer.isEntryScheduled()) { // Skip if the same rotation has been set or we aren't in PIP or haven't actually // entered PIP yet. We still need to update the display layout in the bounds handler // in this case. @@ -124,6 +125,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb // do not forget to update the movement bounds as well. updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */, false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t); + mPipTaskOrganizer.onDisplayRotationSkipped(); return; } // If there is an animation running (ie. from a shelf offset), then ensure that we calculate diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 7e594a43a52f..3c25a13e94eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -130,7 +130,11 @@ public class PipMenuView extends FrameLayout { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); inflate(context, R.layout.pip_menu, this); - mBackgroundDrawable = new ColorDrawable(Color.BLACK); + final boolean enableCornerRadius = mContext.getResources() + .getBoolean(R.bool.config_pipEnableRoundCorner); + mBackgroundDrawable = enableCornerRadius + ? mContext.getDrawable(R.drawable.pip_menu_background) + : new ColorDrawable(Color.BLACK); mBackgroundDrawable.setAlpha(0); mViewRoot = findViewById(R.id.background); mViewRoot.setBackground(mBackgroundDrawable); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt index 98ce2747d532..1f9ff4ab5638 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt @@ -89,6 +89,12 @@ class AppPairsTestCannotPairNonResizeableApps( @Test override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @FlakyTest + @Test + override fun navBarLayerIsAlwaysVisible() { + super.navBarLayerIsAlwaysVisible() + } + @Presubmit @Test fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt index ef68ed630353..87ad8de98702 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt @@ -103,6 +103,12 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps( } } + @FlakyTest + @Test + override fun navBarLayerIsAlwaysVisible() { + super.navBarLayerIsAlwaysVisible() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt index d341bb1e6aa9..a9881485a3c9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt @@ -60,6 +60,12 @@ class RotateTwoLaunchedAppsInAppPairsMode( } } + @FlakyTest + @Test + override fun statusBarLayerIsAlwaysVisible() { + super.statusBarLayerIsAlwaysVisible() + } + @Presubmit @Test fun bothAppWindowsVisible() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index 3bf0296fee20..3396b90f3083 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -74,6 +74,12 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( @Test override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + @FlakyTest + @Test + override fun statusBarLayerIsAlwaysVisible() { + super.statusBarLayerIsAlwaysVisible() + } + @Presubmit @Test fun bothAppWindowsVisible() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt index 83853e61ab5e..512fd9a58ea8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt @@ -17,11 +17,13 @@ package com.android.wm.shell.flicker.apppairs import android.view.Surface +import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.Test abstract class RotateTwoLaunchedAppsTransition( testSpec: FlickerTestParameter @@ -49,4 +51,16 @@ abstract class RotateTwoLaunchedAppsTransition( } } } + + @FlakyTest + @Test + override fun navBarLayerIsAlwaysVisible() { + super.navBarLayerIsAlwaysVisible() + } + + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() { + super.navBarLayerRotatesAndScales() + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt index bca257646e11..e50bde2eaeaa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt @@ -23,7 +23,7 @@ import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.WALLPAPER_TITLE +import com.android.server.wm.flicker.LAUNCHER_TITLE import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -60,9 +60,9 @@ class EnterSplitScreenDockActivity( } override val ignoredWindows: List<String> - get() = listOf(LAUNCHER_PACKAGE_NAME, WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME, + get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME, splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME, - WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME) + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *LAUNCHER_TITLE) @FlakyTest(bugId = 169271943) @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt index 9717709852d4..64cc85340a38 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt @@ -84,7 +84,7 @@ class ExitLegacySplitScreenFromBottom( super.visibleLayersShownMoreThanOneConsecutiveEntry() } - @Presubmit + @FlakyTest @Test fun appWindowBecomesInVisible() = testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt index 3f714bb6b6c9..2e115518721f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt @@ -78,7 +78,7 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( @Test fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible() - @Presubmit + @FlakyTest @Test fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName) @@ -87,7 +87,7 @@ class ExitPrimarySplitScreenShowSecondaryFullscreen( override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() - @Presubmit + @FlakyTest @Test fun appWindowBecomesInVisible() = testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt index 72d6f569ab0c..39f4ce298ff5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt @@ -118,7 +118,7 @@ class LegacySplitScreenToLauncher( fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation) - @Presubmit + @FlakyTest @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt index 8f15e5088914..7cf30ec116eb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt @@ -70,7 +70,7 @@ class OpenAppToLegacySplitScreen( override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() - @Presubmit + @FlakyTest @Test fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage()) @@ -86,7 +86,7 @@ class OpenAppToLegacySplitScreen( @Test fun appPairsDividerBecomesVisible() = testSpec.appPairsDividerBecomesVisible() - @Presubmit + @FlakyTest @Test fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage()) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt index c914adae2b7c..4a59c62ce5d1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt @@ -92,7 +92,7 @@ class RotateOneLaunchedAppAndEnterSplitScreen( @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt index ffb20a4bc99a..834821b4825e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt @@ -89,7 +89,7 @@ class RotateOneLaunchedAppInSplitScreenMode( @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt index 9c798d8ea661..db709a078b70 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt @@ -98,7 +98,7 @@ class RotateTwoLaunchedAppInSplitScreenMode( testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, testSpec.config.endRotation) - @Presubmit + @FlakyTest @Test fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index cd20ddee04fc..2609258e48e0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -54,7 +55,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Presubmit + @FlakyTest @Test fun pipLayerBecomesVisible() { testSpec.assertLayers { @@ -62,7 +63,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Presubmit + @FlakyTest @Test fun pipWindowBecomesVisible() { testSpec.assertWm { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index 2beec2e8c1ed..33ddec376594 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -88,6 +88,12 @@ class EnterPipToOtherOrientationTest( @Test override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @FlakyTest + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + @Presubmit @Test fun pipAppWindowIsAlwaysOnTop() { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt index 0408421c72a5..f290b907d2d3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt @@ -16,11 +16,13 @@ package com.android.wm.shell.flicker.pip +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -41,4 +43,16 @@ class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTr pipApp.closePipWindow(wmHelper) } } + + @FlakyTest + @Test + override fun pipLayerBecomesInvisible() { + super.pipLayerBecomesInvisible() + } + + @FlakyTest + @Test + override fun pipWindowBecomesInvisible() { + super.pipWindowBecomesInvisible() + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt index c7a1c9aac86b..444026208ec1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -67,11 +68,11 @@ class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() - @Presubmit + @FlakyTest @Test override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible() - @Presubmit + @FlakyTest @Test override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 852ee4726080..0d686f514116 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -75,7 +76,7 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, testSpec.config.endRotation, allStates = false) - @Presubmit + @FlakyTest @Test override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index 6f17a2c57ffc..f7f658e52c52 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice @@ -59,7 +58,7 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Presubmit + @FlakyTest @Test fun appReplacesPipWindow() { testSpec.assertWm { @@ -69,7 +68,7 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Presubmit + @FlakyTest @Test fun appReplacesPipLayer() { testSpec.assertLayers { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index ad1ccbd10753..b4c75a6d1165 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -20,6 +20,7 @@ import android.app.Instrumentation import android.content.Intent import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter @@ -167,11 +168,11 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { @Test open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - @Presubmit + @FlakyTest @Test open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Presubmit + @FlakyTest @Test open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index 9aab7f30e6f8..1e7d08bfc0ed 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -125,6 +125,12 @@ class SetRequestedOrientationWhilePinnedTest( } } + @FlakyTest + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index b2a986361b87..24821888e2eb 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -658,10 +658,14 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* cont if (gpuCompleteTime == -1) { gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted); } - if (gpuCompleteTime < frameInfo->get(FrameInfoIndex::SwapBuffers)) { - // TODO (b/180488606): Investigate why this can happen for first frames. - ALOGW("Impossible GPU complete time swapBuffers=%" PRIi64 " gpuComplete=%" PRIi64, - frameInfo->get(FrameInfoIndex::SwapBuffers), gpuCompleteTime); + if (gpuCompleteTime < frameInfo->get(FrameInfoIndex::IssueDrawCommandsStart)) { + // On Vulkan the GPU commands are flushed to the GPU during IssueDrawCommands rather + // than after SwapBuffers. So if the GPU signals before issue draw commands, then + // something probably went wrong. Anything after that could just be expected + // pipeline differences + ALOGW("Impossible GPU complete time issueCommandsStart=%" PRIi64 + " gpuComplete=%" PRIi64, + frameInfo->get(FrameInfoIndex::IssueDrawCommandsStart), gpuCompleteTime); gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted); } frameInfo->set(FrameInfoIndex::FrameCompleted) = gpuCompleteTime; diff --git a/libs/hwui/tests/scripts/skp-capture.sh b/libs/hwui/tests/scripts/skp-capture.sh index 4b46fbf86818..0749a8dcebcb 100755 --- a/libs/hwui/tests/scripts/skp-capture.sh +++ b/libs/hwui/tests/scripts/skp-capture.sh @@ -76,8 +76,8 @@ banner '...WAITING FOR APP INTERACTION...' # so we continue to show the "waiting for app interaction" message as long as the app still requires # interaction to draw more frames. adb_test_file_nonzero() { - # grab first byte of `du` output - X="$(adb shell "du \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" + # grab first byte of `wc -c` output + X="$(adb shell "wc -c \"$1\" 2> /dev/null | dd bs=1 count=1 2> /dev/null")" test "$X" && test "$X" -ne 0 } timeout=$(( $(date +%s) + $phase1_timeout_seconds)) diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 864350e25929..3a19b136b06e 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -2495,7 +2495,10 @@ public final class MediaDrm implements AutoCloseable { * The default security level is defined as the highest security level * supported on the device. * - * @param mime The mime type of the media data + * @param mime The mime type of the media data. Please use {@link + * #isCryptoSchemeSupported(UUID, String)} to query mime type support separately; + * for unsupported mime types the return value of {@link + * #requiresSecureDecoder(String)} is crypto scheme dependent. */ public boolean requiresSecureDecoder(@NonNull String mime) { return requiresSecureDecoder(mime, getMaxSecurityLevel()); @@ -2505,7 +2508,10 @@ public final class MediaDrm implements AutoCloseable { * Query if the crypto scheme requires the use of a secure decoder * to decode data of the given mime type at the given security level. * - * @param mime The mime type of the media data + * @param mime The mime type of the media data. Please use {@link + * #isCryptoSchemeSupported(UUID, String, int)} to query mime type support + * separately; for unsupported mime types the return value of {@link + * #requiresSecureDecoder(String, int)} is crypto scheme dependent. * @param level a security level between {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO} * and {@link #SECURITY_LEVEL_HW_SECURE_ALL}. Otherwise the special value * {@link #getMaxSecurityLevel()} is also permitted; diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index e7d30ebba4b1..9eacc74843f9 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -19,7 +19,6 @@ import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -493,15 +492,26 @@ public final class PlaybackState implements Parcelable { /** * Returns whether this is considered as an active playback state. - * @hide + * <p> + * The playback state is considered as an active if the state is one of the following: + * <ul> + * <li>{@link #STATE_BUFFERING}</li> + * <li>{@link #STATE_CONNECTING}</li> + * <li>{@link #STATE_FAST_FORWARDING}</li> + * <li>{@link #STATE_PLAYING}</li> + * <li>{@link #STATE_REWINDING}</li> + * <li>{@link #STATE_SKIPPING_TO_NEXT}</li> + * <li>{@link #STATE_SKIPPING_TO_PREVIOUS}</li> + * <li>{@link #STATE_SKIPPING_TO_QUEUE_ITEM}</li> + * </ul> */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) - public boolean isActiveState() { + public boolean isActive() { switch (mState) { case PlaybackState.STATE_FAST_FORWARDING: case PlaybackState.STATE_REWINDING: case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: case PlaybackState.STATE_SKIPPING_TO_NEXT: + case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM: case PlaybackState.STATE_BUFFERING: case PlaybackState.STATE_CONNECTING: case PlaybackState.STATE_PLAYING: diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt index 63e0fe945abb..2194575f20ba 100644 --- a/packages/Connectivity/framework/api/system-current.txt +++ b/packages/Connectivity/framework/api/system-current.txt @@ -239,6 +239,7 @@ package android.net { method public final void sendQosSessionLost(int, int, int); method public final void sendSocketKeepaliveEvent(int, int); method @Deprecated public void setLegacySubtype(int, @NonNull String); + method public void setLingerDuration(@NonNull java.time.Duration); method public void setTeardownDelayMs(@IntRange(from=0, to=0x1388) int); method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>); method public void unregister(); diff --git a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl index 26cb1ed6b4b4..9a58add5d2ba 100644 --- a/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl +++ b/packages/Connectivity/framework/src/android/net/INetworkAgentRegistry.aidl @@ -42,4 +42,5 @@ oneway interface INetworkAgentRegistry { void sendQosSessionLost(int qosCallbackId, in QosSession session); void sendQosCallbackError(int qosCallbackId, int exceptionType); void sendTeardownDelayMs(int teardownDelayMs); + void sendLingerDuration(int durationMs); } diff --git a/packages/Connectivity/framework/src/android/net/NetworkAgent.java b/packages/Connectivity/framework/src/android/net/NetworkAgent.java index 3622c1c669db..518d3f39d113 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkAgent.java +++ b/packages/Connectivity/framework/src/android/net/NetworkAgent.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Build; @@ -106,6 +107,9 @@ public abstract class NetworkAgent { private final String LOG_TAG; private static final boolean DBG = true; private static final boolean VDBG = false; + /** @hide */ + @TestApi + public static final int MIN_LINGER_TIMER_MS = 2000; private final ArrayList<RegistryAction> mPreConnectedQueue = new ArrayList<>(); private volatile long mLastBwRefreshTime = 0; private static final long BW_REFRESH_MIN_WIN_MS = 500; @@ -391,6 +395,15 @@ public abstract class NetworkAgent { */ public static final int CMD_NETWORK_DESTROYED = BASE + 23; + /** + * Sent by the NetworkAgent to ConnectivityService to set the linger duration for this network + * agent. + * arg1 = the linger duration, represents by {@link Duration}. + * + * @hide + */ + public static final int EVENT_LINGER_DURATION_CHANGED = BASE + 24; + private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) { final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacySubType, config.legacyTypeName, config.legacySubTypeName); @@ -1287,6 +1300,22 @@ public abstract class NetworkAgent { queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType)); } + /** + * Set the linger duration for this network agent. + * @param duration the delay between the moment the network becomes unneeded and the + * moment the network is disconnected or moved into the background. + * Note that If this duration has greater than millisecond precision, then + * the internal implementation will drop any excess precision. + */ + public void setLingerDuration(@NonNull final Duration duration) { + Objects.requireNonNull(duration); + final long durationMs = duration.toMillis(); + if (durationMs < MIN_LINGER_TIMER_MS || durationMs > Integer.MAX_VALUE) { + throw new IllegalArgumentException("Duration must be within [" + + MIN_LINGER_TIMER_MS + "," + Integer.MAX_VALUE + "]ms"); + } + queueOrSendMessage(ra -> ra.sendLingerDuration((int) durationMs)); + } /** @hide */ protected void log(final String s) { diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java index ba83a44d0d6c..efd336377114 100644 --- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java +++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java @@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.net.NetworkCapabilities.RedactionType; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -45,7 +46,7 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { public final String sessionId; @Override - public long getApplicableRedactions() { + public @RedactionType long getApplicableRedactions() { return REDACT_FOR_NETWORK_SETTINGS; } @@ -53,7 +54,7 @@ public final class VpnTransportInfo implements TransportInfo, Parcelable { * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary. */ @NonNull - public VpnTransportInfo makeCopy(long redactions) { + public VpnTransportInfo makeCopy(@RedactionType long redactions) { return new VpnTransportInfo(type, ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : sessionId); } diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml index dcbdc07d1335..cec8b3294418 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml @@ -15,7 +15,7 @@ limitations under the License. --> <resources> - <!--DEPRECATED. It will remove after all of client team migrated to new style. --> + <!--DEPRECATED. It will be removed after all of client teams migrated to new style. --> <style name="PreferenceTheme" parent="@style/PreferenceThemeOverlay"> <item name="preferenceCategoryStyle">@style/SettingsCategoryPreference</item> <item name="preferenceStyle">@style/SettingsPreference</item> @@ -28,7 +28,7 @@ <item name="footerPreferenceStyle">@style/Preference.Material</item> </style> - <style name="SettingsPreferenceTheme" parent="@style/PreferenceThemeOverlay"> + <style name="PreferenceTheme.SettingsBase" parent="@style/PreferenceThemeOverlay"> <item name="preferenceCategoryStyle">@style/SettingsCategoryPreference</item> <item name="preferenceStyle">@style/SettingsPreference</item> <item name="checkBoxPreferenceStyle">@style/SettingsCheckBoxPreference</item> diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml index 13c7523317fe..9c096d28c5c8 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml @@ -21,7 +21,7 @@ <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle</item> <item name="android:listPreferredItemPaddingStart">@dimen/preference_padding_start</item> <item name="android:listPreferredItemPaddingEnd">@dimen/preference_padding_end</item> - <item name="preferenceTheme">@style/SettingsPreferenceTheme</item> + <item name="preferenceTheme">@style/PreferenceTheme.SettingsBase</item> </style> <!-- Using in SubSettings page including injected settings page --> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index bef6423f3baa..f685d888ffa3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -113,6 +113,8 @@ <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" /> <!-- ACCESS_BACKGROUND_LOCATION is needed for testing purposes only. --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + <!-- ACCESS_MTP is needed for testing purposes only. --> + <uses-permission android:name="android.permission.ACCESS_MTP" /> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> @@ -539,6 +541,9 @@ <!-- Permission required for CTS test - CtsRotationResolverServiceDeviceTestCases --> <uses-permission android:name="android.permission.MANAGE_ROTATION_RESOLVER" /> + <!-- Permission required for CTS test - CtsUwbTestCases --> + <uses-permission android:name="android.permission.UWB_PRIVILEGED" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index da78a7c24d0f..3363f8ea808c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -313,12 +313,14 @@ class ActivityLaunchAnimator(context: Context) { } context.mainExecutor.execute { - startAnimation(remoteAnimationTargets, iRemoteAnimationFinishedCallback) + startAnimation(remoteAnimationTargets, remoteAnimationNonAppTargets, + iRemoteAnimationFinishedCallback) } } private fun startAnimation( remoteAnimationTargets: Array<out RemoteAnimationTarget>, + remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>, iCallback: IRemoteAnimationFinishedCallback ) { val window = remoteAnimationTargets.firstOrNull { @@ -332,7 +334,7 @@ class ActivityLaunchAnimator(context: Context) { return } - val navigationBar = remoteAnimationTargets.firstOrNull { + val navigationBar = remoteAnimationNonAppTargets.firstOrNull { it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR } diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 35423a979cbc..53ff9f0e8c0b 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -58,5 +58,7 @@ public interface BcSmartspaceDataPlugin extends Plugin { /** View to which this plugin can be registered, in order to get updates. */ interface SmartspaceView { void registerDataProvider(BcSmartspaceDataPlugin plugin); + + void setPrimaryTextColor(int color); } } diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 00c27bf5f10d..1cef44b3b3df 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -79,7 +79,6 @@ android:id="@+id/left_aligned_notification_icon_container" android:layout_width="match_parent" android:layout_height="@dimen/notification_shelf_height" - android:layout_marginTop="@dimen/widget_vertical_padding" android:layout_below="@id/keyguard_status_area" android:paddingStart="@dimen/below_clock_padding_start" /> diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml index 76045e4126cb..e5c7352807f8 100644 --- a/packages/SystemUI/res/drawable/qs_detail_background.xml +++ b/packages/SystemUI/res/drawable/qs_detail_background.xml @@ -18,7 +18,7 @@ Copyright (C) 2014 The Android Open Source Project <inset> <shape> <solid android:color="@color/qs_detail_transition"/> - <corners android:radius="?android:attr/dialogCornerRadius" /> + <corners android:radius="@dimen/qs_footer_action_corner_radius" /> </shape> </inset> </item> @@ -26,7 +26,7 @@ Copyright (C) 2014 The Android Open Source Project <inset> <shape> <solid android:color="?android:attr/colorBackgroundFloating"/> - <corners android:radius="?android:attr/dialogCornerRadius" /> + <corners android:radius="@dimen/qs_footer_action_corner_radius" /> </shape> </inset> </item> diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml index 34675ab891c1..6022206c208c 100644 --- a/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml +++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background.xml @@ -23,14 +23,14 @@ <item android:id="@android:id/mask"> <shape android:shape="rectangle"> <solid android:color="@android:color/white"/> - <corners android:radius="@dimen/screenshot_button_corner_radius"/> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> </shape> </item> <item> <shape android:shape="rectangle"> <stroke android:width="1dp" android:color="@color/qs_footer_action_border"/> <solid android:color="@android:color/transparent"/> - <corners android:radius="@dimen/screenshot_button_corner_radius"/> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml b/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml index 596ed711dd43..bbcfb15d9226 100644 --- a/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml +++ b/packages/SystemUI/res/drawable/qs_footer_action_chip_background_borderless.xml @@ -22,13 +22,13 @@ <item android:id="@android:id/mask"> <shape android:shape="rectangle"> <solid android:color="@android:color/white"/> - <corners android:radius="@dimen/screenshot_button_corner_radius"/> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> </shape> </item> <item> <shape android:shape="rectangle"> <solid android:color="@android:color/transparent"/> - <corners android:radius="@dimen/screenshot_button_corner_radius"/> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 2355650907df..ac41a250d4e2 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -656,4 +656,10 @@ <!-- Y --> <!-- radius --> </integer-array> + + <!-- Overrides the behavior of the face unlock keyguard bypass setting: + 0 - Don't override the setting (default) + 1 - Override the setting to always bypass keyguard + 2 - Override the setting to never bypass keyguard --> + <integer name="config_face_unlock_bypass_override">0</integer> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a19d128a9c67..5767174ba603 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -412,6 +412,8 @@ <!-- The size of each of the icon buttons in the QS footer --> <dimen name="qs_footer_action_button_size">@dimen/qs_footer_height</dimen> + <dimen name="qs_footer_action_corner_radius">20dp</dimen> + <!-- (48dp - 44dp) / 2 --> <dimen name="qs_footer_action_inset">2dp</dimen> @@ -1413,17 +1415,17 @@ <dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen> <dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen> - <dimen name="rounded_slider_height">44dp</dimen> + <dimen name="rounded_slider_height">48dp</dimen> <!-- rounded_slider_height / 2 --> - <dimen name="rounded_slider_corner_radius">22dp</dimen> + <dimen name="rounded_slider_corner_radius">24dp</dimen> <dimen name="rounded_slider_icon_size">20dp</dimen> <!-- (rounded_slider_height - rounded_slider_icon_size) / 2 --> - <dimen name="rounded_slider_icon_inset">12dp</dimen> + <dimen name="rounded_slider_icon_inset">14dp</dimen> <!-- rounded_slider_corner_radius - rounded_slider_track_corner_radius --> - <dimen name="rounded_slider_track_inset">18dp</dimen> - <dimen name="rounded_slider_track_width">8dp</dimen> + <dimen name="rounded_slider_track_inset">22dp</dimen> + <dimen name="rounded_slider_track_width">4dp</dimen> <!-- rounded_slider_track_width / 2 --> - <dimen name="rounded_slider_track_corner_radius">4dp</dimen> + <dimen name="rounded_slider_track_corner_radius">2dp</dimen> <!-- inset for ic_lock_open within a DisabledUdfpsView --> <dimen name="udfps_unlock_icon_inset">16dp</dimen> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index af3239e6beb1..1e98f866bd4e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -79,11 +79,6 @@ interface ISystemUiProxy { void startAssistant(in Bundle bundle) = 13; /** - * Creates a new gesture monitor - */ - Bundle monitorGestureInput(String name, int displayId) = 14; - - /** * Notifies that the accessibility button in the system's navigation area has been clicked */ void notifyAccessibilityButtonClicked(int displayId) = 15; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java index 074448d963b1..bf8e6a5663c0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputMonitorCompat.java @@ -16,10 +16,7 @@ package com.android.systemui.shared.system; import android.hardware.input.InputManager; -import android.os.Bundle; import android.os.Looper; -import android.os.Parcel; -import android.os.Parcelable; import android.view.Choreographer; import android.view.InputMonitor; @@ -29,9 +26,8 @@ import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver; /** * @see android.view.InputMonitor */ -public class InputMonitorCompat implements Parcelable { +public class InputMonitorCompat { private final InputMonitor mInputMonitor; - private boolean mForReturn = false; /** * Monitor input on the specified display for gestures. @@ -40,10 +36,6 @@ public class InputMonitorCompat implements Parcelable { mInputMonitor = InputManager.getInstance().monitorGestureInput(name, displayId); } - private InputMonitorCompat(InputMonitor monitor) { - mInputMonitor = monitor; - } - /** * @see InputMonitor#pilferPointers() */ @@ -66,48 +58,4 @@ public class InputMonitorCompat implements Parcelable { return new InputEventReceiver(mInputMonitor.getInputChannel(), looper, choreographer, listener); } - - /** - * Gets the input monitor stored in a bundle - */ - public static InputMonitorCompat fromBundle(Bundle bundle, String key) { - bundle.setClassLoader(InputMonitorCompat.class.getClassLoader()); - return (InputMonitorCompat) bundle.getParcelable(key); - } - - /** - * Gets the input monitor compat as the return value. - */ - public static InputMonitorCompat obtainReturnValue(InputMonitor monitor) { - final InputMonitorCompat monitorCompat = new InputMonitorCompat(monitor); - monitorCompat.mForReturn = true; - return monitorCompat; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - mInputMonitor.writeToParcel(dest, - mForReturn ? PARCELABLE_WRITE_RETURN_VALUE : flags); - } - - private InputMonitorCompat(Parcel in) { - mInputMonitor = InputMonitor.CREATOR.createFromParcel(in); - } - - public static final Creator<InputMonitorCompat> CREATOR = new Creator<InputMonitorCompat>() { - @Override - public InputMonitorCompat createFromParcel(Parcel in) { - return new InputMonitorCompat(in); - } - - @Override - public InputMonitorCompat[] newArray(int size) { - return new InputMonitorCompat[size]; - } - }; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 41840afc4995..927bce08268d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -38,7 +38,6 @@ import java.util.StringJoiner; public class QuickStepContract { public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; - public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor"; public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; // See IPip.aidl diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 032ed7da0a46..f89e365bc995 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -26,14 +26,18 @@ import android.app.smartspace.SmartspaceSession; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.graphics.Color; import android.text.TextUtils; import android.text.format.DateFormat; import android.view.View; import android.widget.FrameLayout; import android.widget.RelativeLayout; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; +import com.android.internal.graphics.ColorUtils; import com.android.keyguard.clock.ClockManager; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -50,6 +54,7 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.ViewController; import java.util.Locale; @@ -87,6 +92,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private Executor mUiExecutor; private SmartspaceSession mSmartspaceSession; private SmartspaceSession.Callback mSmartspaceCallback; + private float mDozeAmount; + private int mWallpaperTextColor; + private int mDozeColor = Color.WHITE; + private ConfigurationController mConfigurationController; /** * Listener for changes to the color palette. @@ -103,8 +112,25 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } }; + private final ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onThemeChanged() { + updateWallpaperColor(); + } + }; + private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; + private final StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onDozeAmountChanged(float linear, float eased) { + mDozeAmount = eased; + updateSmartspaceColor(); + } + }; + // If set, will replace keyguard_status_area private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView; @@ -121,7 +147,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS PluginManager pluginManager, FeatureFlags featureFlags, @Main Executor uiExecutor, - BatteryController batteryController) { + BatteryController batteryController, + ConfigurationController configurationController) { super(keyguardClockSwitch); mResources = resources; mStatusBarStateController = statusBarStateController; @@ -134,6 +161,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mIsSmartspaceEnabled = featureFlags.isSmartspaceEnabled(); mUiExecutor = uiExecutor; mBatteryController = batteryController; + mConfigurationController = configurationController; } /** @@ -172,6 +200,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mBatteryController); mLargeClockViewController.init(); + mDozeAmount = mStatusBarStateController.getDozeAmount(); + updateWallpaperColor(); + + mStatusBarStateController.addCallback(mStatusBarStateListener); + mConfigurationController.addCallback(mConfigurationListener); + // If a smartspace plugin is detected, replace the existing smartspace // (keyguard_status_area), and initialize a new session mPluginListener = new PluginListener<BcSmartspaceDataPlugin>() { @@ -186,8 +220,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mSmartspaceView = plugin.getView(mView); mSmartspaceView.registerDataProvider(plugin); + updateSmartspaceColor(); View asView = (View) mSmartspaceView; + // Place plugin view below normal clock... RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( MATCH_PARENT, WRAP_CONTENT); lp.addRule(RelativeLayout.BELOW, R.id.lockscreen_clock_view); @@ -197,6 +233,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS .getDimensionPixelSize(R.dimen.below_clock_padding_start); asView.setPadding(padding, 0, padding, 0); + // ... but above the large clock + lp = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT); + lp.addRule(RelativeLayout.BELOW, asView.getId()); + mLargeClockFrame.setLayoutParams(lp); + View nic = mView.findViewById( com.android.systemui.R.id.left_aligned_notification_icon_container); lp = (RelativeLayout.LayoutParams) nic.getLayoutParams(); @@ -219,6 +260,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS nic.getLayoutParams(); lp.addRule(RelativeLayout.BELOW, R.id.keyguard_status_area); nic.setLayoutParams(lp); + mLargeClockFrame.setLayoutParams(lp); mSmartspaceView = null; } @@ -235,6 +277,19 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mPluginManager.addPluginListener(mPluginListener, BcSmartspaceDataPlugin.class, false); } + private void updateWallpaperColor() { + mWallpaperTextColor = Utils.getColorAttrDefaultColor(getContext(), + R.attr.wallpaperTextColor); + updateSmartspaceColor(); + } + + private void updateSmartspaceColor() { + if (mSmartspaceView != null) { + int color = ColorUtils.blendARGB(mWallpaperTextColor, mDozeColor, mDozeAmount); + mSmartspaceView.setPrimaryTextColor(color); + } + } + @Override protected void onViewDetached() { if (CUSTOM_CLOCKS_ENABLED) { @@ -249,6 +304,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mSmartspaceSession = null; } mPluginManager.removePluginListener(mPluginListener); + mStatusBarStateController.removeCallback(mStatusBarStateListener); + mConfigurationController.removeCallback(mConfigurationListener); } /** @@ -392,4 +449,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private int getCurrentLayoutDirection() { return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); } + + @VisibleForTesting + ConfigurationController.ConfigurationListener getConfigurationListener() { + return mConfigurationListener; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt index 8cd68ef8acbc..ab15630a6975 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt @@ -18,7 +18,7 @@ data class KeyguardFaceListenModel( val isFaceDisabled: Boolean, val isBecauseCannotSkipBouncer: Boolean, val isKeyguardGoingAway: Boolean, - val isFaceSettingEnabledForUser: Boolean, + val isBiometricSettingEnabledForUser: Boolean, val isLockIconPressed: Boolean, val isScanningAllowedByStrongAuth: Boolean, val isPrimaryUser: Boolean, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 67ee1f45048e..138dd15b33b7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -353,18 +353,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; - private SparseBooleanArray mFaceSettingEnabledForUser = new SparseBooleanArray(); + private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray(); private BiometricManager mBiometricManager; private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback = new IBiometricEnabledOnKeyguardCallback.Stub() { @Override - public void onChanged(BiometricSourceType type, boolean enabled, int userId) - throws RemoteException { + public void onChanged(boolean enabled, int userId) throws RemoteException { mHandler.post(() -> { - if (type == BiometricSourceType.FACE) { - mFaceSettingEnabledForUser.put(userId, enabled); - updateFaceListeningState(); - } + mBiometricEnabledForUser.put(userId, enabled); + updateBiometricListeningState(); }); } }; @@ -1119,6 +1116,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); final boolean isEncrypted = containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT); + return isEncrypted || isLockDown; } @@ -1359,7 +1357,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:AOD_INTERRUPT_END"); } - mAuthController.onCancelAodInterrupt(); + mAuthController.onCancelUdfps(); mIsUdfpsRunningWhileDozing = false; } }; @@ -1631,6 +1629,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } + @VisibleForTesting + void resetBiometricListeningState() { + mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; + mFaceRunningState = BIOMETRIC_STATE_STOPPED; + } + private void registerRingerTracker() { mRingerModeTracker.getRingerMode().observeForever(mRingerModeObserver); } @@ -1951,7 +1955,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mIsFaceEnrolled = whitelistIpcs( () -> mFaceManager != null && mFaceManager.isHardwareDetected() && mFaceManager.hasEnrolledTemplates(userId) - && mFaceSettingEnabledForUser.get(userId)); + && mBiometricEnabledForUser.get(userId)); } /** @@ -2099,7 +2103,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming)) && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser()) && (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser - && allowedOnBouncer; + && allowedOnBouncer && mBiometricEnabledForUser.get(getCurrentUser()); return shouldListen; } @@ -2158,7 +2162,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant()) && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer - && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed + && !mKeyguardGoingAway && mBiometricEnabledForUser.get(user) && !mLockIconPressed && strongAuthAllowsScanning && mIsPrimaryUser && !mSecureCameraLaunched; @@ -2176,7 +2180,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab isFaceDisabled(user), becauseCannotSkipBouncer, mKeyguardGoingAway, - mFaceSettingEnabledForUser.get(user), + mBiometricEnabledForUser.get(user), mLockIconPressed, strongAuthAllowsScanning, mIsPrimaryUser, @@ -3235,6 +3239,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); pw.println(" udfpsEnrolled=" + isUdfpsEnrolled()); + pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId)); if (isUdfpsEnrolled()) { pw.println(" shouldListenForUdfps=" + shouldListenForUdfps()); pw.println(" bouncerVisible=" + mBouncer); @@ -3257,7 +3262,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" possible=" + isUnlockWithFacePossible(userId)); pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); - pw.println(" enabledByUser=" + mFaceSettingEnabledForUser.get(userId)); + pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId)); pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched); } if (mFaceListenModels != null && !mFaceListenModels.isEmpty()) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index ed8f32f31035..0c7b55d6ecbb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -309,18 +309,14 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, } /** - * Cancel a fingerprint scan. - * - * The sensor that triggers an AOD interrupt for fingerprint doesn't give - * ACTION_UP/ACTION_CANCEL events, so the scan needs to be cancelled manually. This should be - * called when authentication either succeeds or fails. Failing to cancel the scan will leave - * the screen in high brightness mode. + * Cancel a fingerprint scan manually. This will get rid of the white circle on the udfps + * sensor area even if the user hasn't explicitly lifted their finger yet. */ - public void onCancelAodInterrupt() { + public void onCancelUdfps() { if (mUdfpsController == null) { return; } - mUdfpsController.onCancelAodInterrupt(); + mUdfpsController.onCancelUdfps(); } private void sendResultAndCleanUp(@DismissedReason int reason, @@ -499,6 +495,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, if (DEBUG) Log.d(TAG, "onBiometricError, hard error: " + errorMessage); mCurrentDialog.onError(errorMessage); } + + onCancelUdfps(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index aa818bfdadeb..9239a8ade615 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -35,15 +36,23 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; +import android.media.AudioAttributes; +import android.os.Handler; +import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.VelocityTracker; +import android.view.View; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; @@ -94,7 +103,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @NonNull private final DumpManager mDumpManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final KeyguardViewMediator mKeyguardViewMediator; - @NonNull private FalsingManager mFalsingManager; + @NonNull private final Vibrator mVibrator; + @NonNull private final Handler mMainHandler; + @NonNull private final FalsingManager mFalsingManager; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -118,6 +129,27 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private boolean mIsAodInterruptActive; @Nullable private Runnable mCancelAodTimeoutAction; + private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = + new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .build(); + + private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK); + private final VibrationEffect mEffectTextureTick = + VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); + private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + private final VibrationEffect mEffectHeavy = + VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + private final Runnable mAcquiredVibration = new Runnable() { + @Override + public void run() { + String effect = Settings.Global.getString(mContext.getContentResolver(), + "udfps_acquired_type"); + mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES); + } + }; + /** * Keeps track of state within a single FingerprintService request. Note that this state * persists across configuration changes, etc, since it is considered a single request. @@ -227,7 +259,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { }; @SuppressLint("ClickableViewAccessibility") - private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) -> { + private final UdfpsView.OnTouchListener mOnTouchListener = this::onTouch; + + private boolean onTouch(View view, MotionEvent event) { UdfpsView udfpsView = (UdfpsView) view; final boolean isFingerDown = udfpsView.isIlluminationRequested(); boolean handled = false; @@ -251,6 +285,27 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // data for many other pointers because of multi-touch support. mActivePointerId = event.getPointerId(0); mVelocityTracker.addMovement(event); + + // TODO: (b/185124905) these settings are for ux testing purposes and should + // be removed (or cached) before going into production + final ContentResolver contentResolver = mContext.getContentResolver(); + int startEnabled = Settings.Global.getInt(contentResolver, + "udfps_start", 0); + if (startEnabled > 0) { + String startEffectSetting = Settings.Global.getString(contentResolver, + "udfps_start_type"); + mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick), + VIBRATION_SONIFICATION_ATTRIBUTES); + } + + int acquiredEnabled = Settings.Global.getInt(contentResolver, + "udfps_acquired", 0); + if (acquiredEnabled > 0) { + int delay = Settings.Global.getInt(contentResolver, + "udfps_acquired_delay", 500); + mMainHandler.removeCallbacks(mAcquiredVibration); + mMainHandler.postDelayed(mAcquiredVibration, delay); + } handled = true; } break; @@ -307,7 +362,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Do nothing. } return handled; - }; + } @Inject public UdfpsController(@NonNull Context context, @@ -324,6 +379,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @NonNull KeyguardViewMediator keyguardViewMediator, @NonNull FalsingManager falsingManager) { mContext = context; + // TODO (b/185124905): inject main handler and vibrator once done prototyping + mMainHandler = new Handler(Looper.getMainLooper()); + mVibrator = context.getSystemService(Vibrator.class); mInflater = inflater; // The fingerprint manager is queried for UDFPS before this class is constructed, so the // fingerprint manager should never be null. @@ -559,19 +617,25 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL, // we need to be careful about not letting the screen accidentally remain in high brightness // mode. As a mitigation, queue a call to cancel the fingerprint scan. - mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelAodInterrupt, + mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps, AOD_INTERRUPT_TIMEOUT_MILLIS); // using a hard-coded value for major and minor until it is available from the sensor onFingerDown(screenX, screenY, minor, major); } /** - * Cancel fingerprint scan. + * Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before + * user explicitly lifts their finger. Generally, this should be called whenever udfps fails + * or errors. * - * This is intended to be called after the fingerprint scan triggered by the AOD interrupt - * either succeeds or fails. + * The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give + * ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually. + * This should be called when authentication either succeeds or fails. Failing to cancel the + * scan will leave the screen in high brightness mode and will show the HbmSurfaceView until + * the user lifts their finger. */ - void onCancelAodInterrupt() { + void onCancelUdfps() { + onFingerUp(); if (!mIsAodInterruptActive) { return; } @@ -580,7 +644,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mCancelAodTimeoutAction = null; } mIsAodInterruptActive = false; - onFingerUp(); } // This method can be called from the UI thread. @@ -598,6 +661,7 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // This method can be called from the UI thread. private void onFingerUp() { + mMainHandler.removeCallbacks(mAcquiredVibration); if (mView == null) { Log.w(TAG, "Null view in onFingerUp"); return; @@ -617,4 +681,23 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // Do nothing. This method can be implemented for devices that require the high-brightness // mode for fingerprint illumination. } + + private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) { + if (TextUtils.isEmpty(effect)) { + return defaultEffect; + } + + switch (effect.toLowerCase()) { + case "click": + return mEffectClick; + case "heavy": + return mEffectHeavy; + case "texture_tick": + return mEffectTextureTick; + case "tick": + return mEffectTick; + default: + return defaultEffect; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java index 1e86cf1d0171..9d47bbb03380 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java @@ -121,6 +121,7 @@ public class WirelessChargingAnimation { params.setFitInsetsTypes(0 /* ignore all system bar insets */); params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + params.setTrustedOverlay(); if (looper == null) { // Use Looper.myLooper() if looper is not specified. diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index ce5795ccd339..767d7ab5a490 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -35,12 +35,10 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.plugins.GlobalActions; -import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; @@ -50,18 +48,17 @@ import dagger.Lazy; public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { private final Context mContext; - private final Lazy<GlobalActionsDialog> mGlobalActionsDialogLazy; + private final Lazy<GlobalActionsDialogLite> mGlobalActionsDialogLazy; private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; - private final ExtensionController.Extension<GlobalActionsPanelPlugin> mWalletPluginProvider; private final BlurUtils mBlurUtils; private final CommandQueue mCommandQueue; - private GlobalActionsDialog mGlobalActionsDialog; + private GlobalActionsDialogLite mGlobalActionsDialog; private boolean mDisabled; @Inject public GlobalActionsImpl(Context context, CommandQueue commandQueue, - Lazy<GlobalActionsDialog> globalActionsDialogLazy, BlurUtils blurUtils) { + Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils) { mContext = context; mGlobalActionsDialogLazy = globalActionsDialogLazy; mKeyguardStateController = Dependency.get(KeyguardStateController.class); @@ -69,10 +66,6 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks mCommandQueue = commandQueue; mBlurUtils = blurUtils; mCommandQueue.addCallback(this); - mWalletPluginProvider = Dependency.get(ExtensionController.class) - .newExtension(GlobalActionsPanelPlugin.class) - .withPlugin(GlobalActionsPanelPlugin.class) - .build(); } @Override @@ -89,8 +82,7 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks if (mDisabled) return; mGlobalActionsDialog = mGlobalActionsDialogLazy.get(); mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(), - mDeviceProvisionedController.isDeviceProvisioned(), - mWalletPluginProvider.get()); + mDeviceProvisionedController.isDeviceProvisioned()); Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth(); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 6d1109ee5f51..3544f60601a8 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -17,14 +17,18 @@ package com.android.systemui.navigationbar; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.os.SystemProperties; +import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -80,6 +84,8 @@ public class NavigationBarController implements Callbacks, ConfigurationController.ConfigurationListener, NavigationModeController.ModeChangedListener, Dumpable { + private static final float TABLET_MIN_DPS = 600; + private static final String TAG = NavigationBarController.class.getSimpleName(); private final Context mContext; @@ -107,6 +113,8 @@ public class NavigationBarController implements Callbacks, private final Handler mHandler; private final DisplayManager mDisplayManager; private final NavigationBarOverlayController mNavBarOverlayController; + private int mNavMode; + private boolean mIsTablet; /** A displayId - nav bar maps. */ @VisibleForTesting @@ -172,11 +180,30 @@ public class NavigationBarController implements Callbacks, configurationController.addCallback(this); mConfigChanges.applyNewConfig(mContext.getResources()); mNavBarOverlayController = navBarOverlayController; + mNavMode = mNavigationModeController.addListener(this); mNavigationModeController.addListener(this); } @Override public void onConfigChanged(Configuration newConfig) { + boolean isOldConfigTablet = mIsTablet; + mIsTablet = isTablet(newConfig); + boolean largeScreenChanged = mIsTablet != isOldConfigTablet; + // If we folded/unfolded while in 3 button, show navbar in folded state, hide in unfolded + if (isThreeButtonTaskbarFlagEnabled() && + largeScreenChanged && mNavMode == NAV_BAR_MODE_3BUTTON) { + if (!mIsTablet) { + // Folded state, show 3 button nav bar + createNavigationBar(mContext.getDisplay(), null, null); + } else { + // Unfolded state, hide 3 button nav bars + for (int i = 0; i < mNavigationBars.size(); i++) { + removeNavigationBar(mNavigationBars.keyAt(i)); + } + } + return; + } + if (mConfigChanges.applyNewConfig(mContext.getResources())) { for (int i = 0; i < mNavigationBars.size(); i++) { recreateNavigationBar(mNavigationBars.keyAt(i)); @@ -190,7 +217,19 @@ public class NavigationBarController implements Callbacks, @Override public void onNavigationModeChanged(int mode) { + final int oldMode = mNavMode; + mNavMode = mode; mHandler.post(() -> { + // create/destroy nav bar based on nav mode only in unfolded state + if (isThreeButtonTaskbarFlagEnabled() && oldMode != mNavMode && mIsTablet) { + if (oldMode == NAV_BAR_MODE_3BUTTON && + mNavigationBars.get(mContext.getDisplayId()) == null) { + // We remove navbar for 3 button unfolded, add it back in + createNavigationBar(mContext.getDisplay(), null, null); + } else if (mNavMode == NAV_BAR_MODE_3BUTTON) { + removeNavigationBar(mContext.getDisplayId()); + } + } for (int i = 0; i < mNavigationBars.size(); i++) { NavigationBar navBar = mNavigationBars.valueAt(i); if (navBar == null) { @@ -212,6 +251,7 @@ public class NavigationBarController implements Callbacks, @Override public void onDisplayReady(int displayId) { Display display = mDisplayManager.getDisplay(displayId); + mIsTablet = isTablet(mContext.getResources().getConfiguration()); createNavigationBar(display, null /* savedState */, null /* result */); } @@ -267,6 +307,10 @@ public class NavigationBarController implements Callbacks, return; } + if (isThreeButtonTaskbarEnabled()) { + return; + } + final int displayId = display.getDisplayId(); final boolean isOnDefaultDisplay = displayId == DEFAULT_DISPLAY; final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); @@ -398,6 +442,24 @@ public class NavigationBarController implements Callbacks, return mNavigationBars.get(DEFAULT_DISPLAY); } + private boolean isThreeButtonTaskbarEnabled() { + return mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON && + isThreeButtonTaskbarFlagEnabled(); + } + + private boolean isThreeButtonTaskbarFlagEnabled() { + return SystemProperties.getBoolean("persist.debug.taskbar_three_button", false); + } + + private boolean isTablet(Configuration newConfig) { + float density = Resources.getSystem().getDisplayMetrics().density; + int size = Math.min((int) (density * newConfig.screenWidthDp), + (int) (density* newConfig.screenHeightDp)); + DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); + float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; + return (size / densityRatio) >= TABLET_MIN_DPS; + } + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { for (int i = 0; i < mNavigationBars.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 2ca6a92ecc80..01c80f6d33fe 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -281,7 +281,7 @@ public class NavigationBarView extends FrameLayout implements // When in gestural and the IME is showing, don't use the nearest region since it will take // gesture space away from the IME info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - info.touchableRegion.set(getButtonLocations(false /* includeFloatingRotationButton */, + info.touchableRegion.set(getButtonLocations(false /* includeFloatingButtons */, false /* inScreen */, false /* useNearestRegion */)); }; @@ -982,7 +982,7 @@ public class NavigationBarView extends FrameLayout implements */ public void notifyActiveTouchRegions() { mOverviewProxyService.onActiveNavBarRegionChanges( - getButtonLocations(true /* includeFloatingRotationButton */, true /* inScreen */, + getButtonLocations(true /* includeFloatingButtons */, true /* inScreen */, true /* useNearestRegion */)); } @@ -995,14 +995,14 @@ public class NavigationBarView extends FrameLayout implements } /** - * @param includeFloatingRotationButton Whether to include the floating rotation button in the - * region for all the buttons + * @param includeFloatingButtons Whether to include the floating rotation and overlay button in + * the region for all the buttons * @param inScreenSpace Whether to return values in screen space or window space * @param useNearestRegion Whether to use the nearest region instead of the actual button bounds * @return */ - private Region getButtonLocations(boolean includeFloatingRotationButton, - boolean inScreenSpace, boolean useNearestRegion) { + private Region getButtonLocations(boolean includeFloatingButtons, boolean inScreenSpace, + boolean useNearestRegion) { if (useNearestRegion && !inScreenSpace) { // We currently don't support getting the nearest region in anything but screen space useNearestRegion = false; @@ -1014,13 +1014,13 @@ public class NavigationBarView extends FrameLayout implements updateButtonLocation(getRecentsButton(), inScreenSpace, useNearestRegion); updateButtonLocation(getImeSwitchButton(), inScreenSpace, useNearestRegion); updateButtonLocation(getAccessibilityButton(), inScreenSpace, useNearestRegion); - if (includeFloatingRotationButton && mFloatingRotationButton.isVisible()) { + if (includeFloatingButtons && mFloatingRotationButton.isVisible()) { // Note: this button is floating so the nearest region doesn't apply updateButtonLocation(mFloatingRotationButton.getCurrentView(), inScreenSpace); } else { updateButtonLocation(getRotateSuggestionButton(), inScreenSpace, useNearestRegion); } - if (mNavBarOverlayController.isNavigationBarOverlayEnabled() + if (includeFloatingButtons && mNavBarOverlayController.isNavigationBarOverlayEnabled() && mNavBarOverlayController.isVisible()) { // Note: this button is floating so the nearest region doesn't apply updateButtonLocation(mNavBarOverlayController.getCurrentView(), inScreenSpace); diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index afbb197c4a12..6d23739b9c78 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -24,7 +24,6 @@ import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS; @@ -62,7 +61,6 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; import android.view.InputDevice; -import android.view.InputMonitor; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; @@ -89,7 +87,6 @@ import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -358,24 +355,6 @@ public class OverviewProxyService extends CurrentUserTracker implements } @Override - public Bundle monitorGestureInput(String name, int displayId) { - if (!verifyCaller("monitorGestureInput")) { - return null; - } - final long token = Binder.clearCallingIdentity(); - try { - final InputMonitor monitor = - InputManager.getInstance().monitorGestureInput(name, displayId); - final Bundle result = new Bundle(); - result.putParcelable(KEY_EXTRA_INPUT_MONITOR, - InputMonitorCompat.obtainReturnValue(monitor)); - return result; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override public void notifyAccessibilityButtonClicked(int displayId) { if (!verifyCaller("notifyAccessibilityButtonClicked")) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt index fd060e6d486d..718a85a98877 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt @@ -58,6 +58,7 @@ class WiredChargingRippleController @Inject constructor( title = "Wired Charging Animation" flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) + setTrustedOverlay() } @VisibleForTesting @@ -103,7 +104,10 @@ class WiredChargingRippleController @Inject constructor( } fun startRipple() { - if (rippleView.rippleInProgress) { + if (rippleView.rippleInProgress || rippleView.parent != null) { + // Skip if ripple is still playing, or not playing but already added the parent + // (which might happen just before the animation starts or right after + // the animation ends.) return } val mWM = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 8fae7200b56f..500838fac5f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -71,7 +71,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; -import com.android.internal.widget.MessagingLayout; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -658,10 +657,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView boolean beforeS = mEntry.targetSdk < Build.VERSION_CODES.S; int smallHeight; - View expandedView = layout.getExpandedChild(); - boolean isMediaLayout = expandedView != null - && expandedView.findViewById(com.android.internal.R.id.media_actions) != null; - boolean isMessagingLayout = contractedView instanceof MessagingLayout; boolean isCallLayout = contractedView instanceof CallLayout; if (customView && beforeS && !mIsSummaryWithChildren) { @@ -672,12 +667,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else { smallHeight = mMaxSmallHeightBeforeS; } - } else if (isMessagingLayout) { - // TODO(b/173204301): MessagingStyle notifications currently look broken when we enforce - // the standard notification height, so we have to afford them more vertical space to - // make sure we don't crop them terribly. We actually need to revisit this and give - // them a headerless design, then remove this hack. - smallHeight = mMaxSmallHeightLarge; } else if (isCallLayout) { smallHeight = mMaxExpandedHeight; } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt index 383bb7e41a91..08981f1215f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt @@ -23,12 +23,9 @@ import com.android.internal.widget.CachingIconView import com.android.internal.widget.ConversationLayout import com.android.internal.widget.MessagingLinearLayout import com.android.systemui.R -import com.android.systemui.statusbar.TransformableView -import com.android.systemui.statusbar.ViewTransformationHelper import com.android.systemui.statusbar.notification.NotificationUtils -import com.android.systemui.statusbar.notification.TransformState import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.notification.row.HybridNotificationView +import com.android.systemui.statusbar.notification.row.wrapper.NotificationMessagingTemplateViewWrapper.setCustomImageMessageTransform /** * Wraps a notification containing a conversation template @@ -93,33 +90,7 @@ class NotificationConversationTemplateViewWrapper constructor( appName, conversationTitleView) - // Let's ignore the image message container since that is transforming as part of the - // messages already - mTransformationHelper.setCustomTransformation( - object : ViewTransformationHelper.CustomTransformation() { - override fun transformTo( - ownState: TransformState, - otherView: TransformableView, - transformationAmount: Float - ): Boolean { - if (otherView is HybridNotificationView) { - return false - } - // we're hidden by default by the transformState - ownState.ensureVisible() - // Let's do nothing otherwise, this is already handled by the messages - return true - } - - override fun transformFrom( - ownState: TransformState, - otherView: TransformableView, - transformationAmount: Float - ): Boolean = - transformTo(ownState, otherView, transformationAmount) - }, - imageMessageContainer.id - ) + setCustomImageMessageTransform(mTransformationHelper, imageMessageContainer) addViewsTransformingToSimilar( conversationIconView, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java index c9a274294d16..c587ce05b13f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapper.java @@ -18,12 +18,17 @@ package com.android.systemui.statusbar.notification.row.wrapper; import android.content.Context; import android.view.View; +import android.view.ViewGroup; import com.android.internal.widget.MessagingLayout; import com.android.internal.widget.MessagingLinearLayout; import com.android.systemui.R; +import com.android.systemui.statusbar.TransformableView; +import com.android.systemui.statusbar.ViewTransformationHelper; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.TransformState; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.HybridNotificationView; /** * Wraps a notification containing a messaging template @@ -31,12 +36,17 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow public class NotificationMessagingTemplateViewWrapper extends NotificationTemplateViewWrapper { private final int mMinHeightWithActions; + private final View mTitle; + private final View mTitleInHeader; private MessagingLayout mMessagingLayout; private MessagingLinearLayout mMessagingLinearLayout; + private ViewGroup mImageMessageContainer; protected NotificationMessagingTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { super(ctx, view, row); + mTitle = mView.findViewById(com.android.internal.R.id.title); + mTitleInHeader = mView.findViewById(com.android.internal.R.id.header_text_secondary); mMessagingLayout = (MessagingLayout) view; mMinHeightWithActions = NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_messaging_actions_min_height); @@ -44,6 +54,7 @@ public class NotificationMessagingTemplateViewWrapper extends NotificationTempla private void resolveViews() { mMessagingLinearLayout = mMessagingLayout.getMessagingLinearLayout(); + mImageMessageContainer = mMessagingLayout.getImageMessageContainer(); } @Override @@ -59,8 +70,48 @@ public class NotificationMessagingTemplateViewWrapper extends NotificationTempla // This also clears the existing types super.updateTransformedTypes(); if (mMessagingLinearLayout != null) { - mTransformationHelper.addTransformedView(mMessagingLinearLayout.getId(), - mMessagingLinearLayout); + mTransformationHelper.addTransformedView(mMessagingLinearLayout); + } + // The title is not as important for messaging, and stays in the header when expanded, + // but this ensures it animates cleanly between the two positions + if (mTitle == null && mTitleInHeader != null) { + mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, + mTitleInHeader); + } + setCustomImageMessageTransform(mTransformationHelper, mImageMessageContainer); + } + + static void setCustomImageMessageTransform( + ViewTransformationHelper transformationHelper, ViewGroup imageMessageContainer) { + if (imageMessageContainer != null) { + // Let's ignore the image message container since that is transforming as part of the + // messages already. This is also required to prevent a clipping artifact caused by the + // alpha layering triggering hardware rendering mode that in turn results in more + // aggressive clipping than we want. + transformationHelper.setCustomTransformation( + new ViewTransformationHelper.CustomTransformation() { + @Override + public boolean transformTo( + TransformState ownState, + TransformableView otherView, + float transformationAmount) { + if (otherView instanceof HybridNotificationView) { + return false; + } + // we're hidden by default by the transformState + ownState.ensureVisible(); + // Let's do nothing otherwise, this is already handled by the messages + return true; + } + + @Override + public boolean transformFrom( + TransformState ownState, + TransformableView otherView, + float transformationAmount) { + return transformTo(ownState, otherView, transformationAmount); + } + }, imageMessageContainer.getId()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index e0b58125aabd..48f34b3b7716 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -56,6 +56,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp private ProgressBar mProgressBar; private TextView mTitle; private TextView mText; + protected View mSmartReplyContainer; protected View mActionsContainer; private int mContentHeight; @@ -160,6 +161,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp // It's still a viewstub mProgressBar = null; } + mSmartReplyContainer = mView.findViewById(com.android.internal.R.id.smart_reply_container); mActionsContainer = mView.findViewById(com.android.internal.R.id.actions_container); mActions = mView.findViewById(com.android.internal.R.id.actions); mRemoteInputHistory = mView.findViewById( @@ -275,6 +277,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp mProgressBar); } addViewsTransformingToSimilar(mLeftIcon); + addTransformedViews(mSmartReplyContainer); } @Override 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 707135c3d95b..30d9841e41ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -16,11 +16,13 @@ package com.android.systemui.statusbar.phone +import android.annotation.IntDef import android.content.Context import android.content.pm.PackageManager import android.hardware.biometrics.BiometricSourceType import android.provider.Settings import com.android.systemui.Dumpable +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -37,9 +39,18 @@ open class KeyguardBypassController : Dumpable { private val mKeyguardStateController: KeyguardStateController private val statusBarStateController: StatusBarStateController + @BypassOverride private val bypassOverride: Int private var hasFaceFeature: Boolean private var pendingUnlock: PendingUnlock? = null + @IntDef( + FACE_UNLOCK_BYPASS_NO_OVERRIDE, + FACE_UNLOCK_BYPASS_ALWAYS, + FACE_UNLOCK_BYPASS_NEVER + ) + @Retention(AnnotationRetention.SOURCE) + private annotation class BypassOverride + /** * Pending unlock info: * @@ -60,7 +71,14 @@ open class KeyguardBypassController : Dumpable { * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. */ var bypassEnabled: Boolean = false - get() = field && mKeyguardStateController.isFaceAuthEnabled + get() { + val enabled = when (bypassOverride) { + FACE_UNLOCK_BYPASS_ALWAYS -> true + FACE_UNLOCK_BYPASS_NEVER -> false + else -> field + } + return enabled && mKeyguardStateController.isFaceAuthEnabled + } private set var bouncerShowing: Boolean = false @@ -86,6 +104,8 @@ open class KeyguardBypassController : Dumpable { this.mKeyguardStateController = keyguardStateController this.statusBarStateController = statusBarStateController + bypassOverride = context.resources.getInteger(R.integer.config_face_unlock_bypass_override) + hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE) if (!hasFaceFeature) { return @@ -198,5 +218,9 @@ open class KeyguardBypassController : Dumpable { companion object { const val BYPASS_PANEL_FADE_DURATION = 67 + + private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0 + private const val FACE_UNLOCK_BYPASS_ALWAYS = 1 + private const val FACE_UNLOCK_BYPASS_NEVER = 2 } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 55b80dd69b6a..db7736620c26 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -633,7 +633,7 @@ public class BubblesManager implements Dumpable { } else { mNotificationGroupManager.onEntryRemoved(entry); } - }); + }, mSysuiMainExecutor); } /** diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 9017dd244fb4..a8709150c3e4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,6 +32,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.util.AttributeSet; import android.view.View; +import android.widget.FrameLayout; import android.widget.RelativeLayout; import com.android.internal.colorextraction.ColorExtractor; @@ -48,6 +50,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.NotificationIconContainer; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; @@ -98,9 +101,14 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { @Mock private AnimatableClockView mLargeClockView; @Mock + private FrameLayout mLargeClockFrame; + @Mock BatteryController mBatteryController; + @Mock + ConfigurationController mConfigurationController; private KeyguardClockSwitchController mController; + private View mStatusArea; @Before public void setup() { @@ -108,10 +116,13 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { when(mView.findViewById(R.id.left_aligned_notification_icon_container)) .thenReturn(mNotificationIcons); + when(mNotificationIcons.getLayoutParams()).thenReturn( + mock(RelativeLayout.LayoutParams.class)); when(mView.getContext()).thenReturn(getContext()); when(mView.findViewById(R.id.animatable_clock_view)).thenReturn(mClockView); when(mView.findViewById(R.id.animatable_clock_view_large)).thenReturn(mLargeClockView); + when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame); when(mClockView.getContext()).thenReturn(getContext()); when(mLargeClockView.getContext()).thenReturn(getContext()); @@ -131,10 +142,15 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mPluginManager, mFeatureFlags, mExecutor, - mBatteryController); + mBatteryController, + mConfigurationController); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); + + mStatusArea = mock(View.class); + when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea); + } @Test @@ -197,39 +213,40 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { public void testSmartspacePluginConnectedRemovesKeyguardStatusArea() { mController.init(); - View statusArea = mock(View.class); - when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea); - - View nic = mock(View.class); - when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic); - when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class)); - BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class); TestView view = mock(TestView.class); when(plugin.getView(any())).thenReturn(view); mController.mPluginListener.onPluginConnected(plugin, mContext); - verify(statusArea).setVisibility(View.GONE); + verify(mStatusArea).setVisibility(View.GONE); } @Test public void testSmartspacePluginDisconnectedShowsKeyguardStatusArea() { mController.init(); - View statusArea = mock(View.class); - when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea); + BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class); + TestView view = mock(TestView.class); + when(plugin.getView(any())).thenReturn(view); + + mController.mPluginListener.onPluginConnected(plugin, mContext); + mController.mPluginListener.onPluginDisconnected(plugin); + verify(mStatusArea).setVisibility(View.VISIBLE); + } - View nic = mock(View.class); - when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic); - when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class)); + @Test + public void testThemeChangeNotifiesSmartspace() { + mController.init(); BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class); TestView view = mock(TestView.class); when(plugin.getView(any())).thenReturn(view); mController.mPluginListener.onPluginConnected(plugin, mContext); - mController.mPluginListener.onPluginDisconnected(plugin); - verify(statusArea).setVisibility(View.VISIBLE); + + reset(view); + mController.getConfigurationListener().onThemeChanged(); + verify(view).setPrimaryTextColor(anyInt()); } private void verifyAttachment(VerificationMode times) { @@ -246,5 +263,7 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { } public void registerDataProvider(BcSmartspaceDataPlugin plugin) { } + + public void setPrimaryTextColor(int color) { } } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index d544f7378f8a..42cc1fa99909 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -49,7 +49,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.hardware.biometrics.BiometricManager; -import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.face.FaceManager; @@ -188,8 +187,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager); doAnswer(invocation -> { IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0); - callback.onChanged(BiometricSourceType.FACE, true /* enabled */, - KeyguardUpdateMonitor.getCurrentUser()); + callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser()); return null; }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any()); when(mFaceManager.isHardwareDetected()).thenReturn(true); @@ -495,6 +493,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void testFingerprintWhenStrongAuth(int strongAuth) { + // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) + // will trigger updateBiometricListeningState(); + clearInvocations(mFingerprintManager); + mKeyguardUpdateMonitor.resetBiometricListeningState(); + when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth); mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); mTestableLooper.processAllMessages(); @@ -537,7 +540,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { authCallback.onAuthenticationFailed(); // THEN aod interrupt is cancelled - verify(mAuthController).onCancelAodInterrupt(); + verify(mAuthController).onCancelUdfps(); } @Test @@ -557,7 +560,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { authCallback.onAuthenticationError(0, ""); // THEN aod interrupt is cancelled - verify(mAuthController).onCancelAodInterrupt(); + verify(mAuthController).onCancelUdfps(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index bbd3ce89b997..0aa182fb1e81 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -259,7 +259,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it is cancelled - mUdfpsController.onCancelAodInterrupt(); + mUdfpsController.onCancelUdfps(); // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } diff --git a/services/Android.bp b/services/Android.bp index ad1406c73c2f..20b89de7f2a6 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -65,6 +65,7 @@ filegroup { ":services.texttospeech-sources", ":services.usage-sources", ":services.usb-sources", + ":services.uwb-sources", ":services.voiceinteraction-sources", ":services.wifi-sources", ], @@ -129,6 +130,7 @@ java_library { "services.texttospeech", "services.usage", "services.usb", + "services.uwb", "services.voiceinteraction", "services.wifi", "service-blobstore", diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index a230f808ee9e..f8b770b4ec84 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -456,6 +456,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } : null; } + void newAutofillRequestLocked(@Nullable InlineSuggestionsRequest inlineRequest) { + mPendingFillRequest = null; + mWaitForInlineRequest = inlineRequest != null; + mPendingInlineSuggestionsRequest = inlineRequest; + } + void maybeRequestFillLocked() { if (mPendingFillRequest == null) { return; @@ -886,6 +892,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Now request the assist structure data. + requestAssistStructureLocked(requestId, flags); + } + + @GuardedBy("mLock") + private void requestAssistStructureLocked(int requestId, int flags) { try { final Bundle receiverExtras = new Bundle(); receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); @@ -1052,12 +1063,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestLog != null) { requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1); } - processNullResponseLocked(requestId, requestFlags); + processNullResponseOrFallbackLocked(requestId, requestFlags); return; } fieldClassificationIds = response.getFieldClassificationIds(); - if (fieldClassificationIds != null && !mService.isFieldClassificationEnabledLocked()) { + if (!mSessionFlags.mClientSuggestionsEnabled && fieldClassificationIds != null + && !mService.isFieldClassificationEnabledLocked()) { Slog.w(TAG, "Ignoring " + response + " because field detection is disabled"); processNullResponseLocked(requestId, requestFlags); return; @@ -1136,6 +1148,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + @GuardedBy("mLock") + private void processNullResponseOrFallbackLocked(int requestId, int flags) { + if (!mSessionFlags.mClientSuggestionsEnabled) { + processNullResponseLocked(requestId, flags); + return; + } + + // fallback to the default platform password manager + mSessionFlags.mClientSuggestionsEnabled = false; + + final InlineSuggestionsRequest inlineRequest = + (mLastInlineSuggestionsRequest != null + && mLastInlineSuggestionsRequest.first == requestId) + ? mLastInlineSuggestionsRequest.second : null; + mAssistReceiver.newAutofillRequestLocked(inlineRequest); + requestAssistStructureLocked(requestId, + flags & ~FLAG_ENABLED_CLIENT_SUGGESTIONS); + return; + } + // FillServiceCallbacks @Override public void onFillRequestFailure(int requestId, @Nullable CharSequence message) { diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index c49b8e8e77c6..78610a2857bd 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -138,6 +138,8 @@ public class BinderCallsStatsService extends Binder { private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data"; private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY = "latency_observer_sampling_interval"; + private static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY = + "latency_observer_push_interval_minutes"; private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY = "latency_histogram_bucket_count"; private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY = @@ -218,7 +220,9 @@ public class BinderCallsStatsService extends Binder { mParser.getFloat( SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY, BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT)); - + binderLatencyObserver.setPushInterval(mParser.getInt( + SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY, + BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT)); final boolean enabled = mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d9cc4b41f797..4d96162149e2 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1353,8 +1353,8 @@ public class ConnectivityService extends IConnectivityManager.Stub new NetworkInfo(TYPE_NONE, 0, "", ""), new LinkProperties(), new NetworkCapabilities(), new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, - new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mQosCallbackTracker, - mDeps); + new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, + mLingerDelayMs, mQosCallbackTracker, mDeps); } private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { @@ -3167,6 +3167,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { logwtf(nai.toShortString() + " set invalid teardown delay " + msg.arg1); } + break; + } + case NetworkAgent.EVENT_LINGER_DURATION_CHANGED: { + nai.setLingerDuration((int) arg.second); + break; } } } @@ -6516,7 +6521,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkAgentInfo nai = new NetworkAgentInfo(na, new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), - this, mNetd, mDnsResolver, providerId, uid, mQosCallbackTracker, mDeps); + this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs, + mQosCallbackTracker, mDeps); // Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says. processCapabilitiesFromAgent(nai, nc); @@ -7759,7 +7765,7 @@ public class ConnectivityService extends IConnectivityManager.Stub log(" accepting network in place of " + previousSatisfier.toShortString()); } previousSatisfier.removeRequest(previousRequest.requestId); - previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs); + previousSatisfier.lingerRequest(previousRequest.requestId, now); } else { if (VDBG || DDBG) log(" accepting network in place of null"); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 611fe7ad8c78..98bfa28d6415 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -376,7 +376,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private final LocalLog mListenLog = new LocalLog(200); - private List<PhysicalChannelConfig> mPhysicalChannelConfigs; + private List<List<PhysicalChannelConfig>> mPhysicalChannelConfigs; private boolean[] mIsDataEnabled; @@ -716,7 +716,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos[i] = null; mIsDataEnabled[i] = false; mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; - mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); + mPhysicalChannelConfigs.add(i, new ArrayList<>()); mAllowedNetworkTypeReason[i] = -1; mAllowedNetworkTypeValue[i] = -1; mLinkCapacityEstimateLists.add(i, new ArrayList<>()); @@ -816,7 +816,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos[i] = null; mIsDataEnabled[i] = false; mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; - mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); + mPhysicalChannelConfigs.add(i, new ArrayList<>()); mAllowedNetworkTypeReason[i] = -1; mAllowedNetworkTypeValue[i] = -1; mLinkCapacityEstimateLists.add(i, new ArrayList<>()); @@ -1314,8 +1314,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { r.callback.onPhysicalChannelConfigChanged( shouldSanitizeLocationForPhysicalChannelConfig(r) - ? getLocationSanitizedConfigs(mPhysicalChannelConfigs) - : mPhysicalChannelConfigs); + ? getLocationSanitizedConfigs( + mPhysicalChannelConfigs.get(phoneId)) + : mPhysicalChannelConfigs.get(phoneId)); } catch (RemoteException ex) { remove(r.binder); } @@ -2568,7 +2569,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { int phoneId = SubscriptionManager.getPhoneId(subId); if (validatePhoneId(phoneId)) { - mPhysicalChannelConfigs.set(phoneId, configs.get(phoneId)); + mPhysicalChannelConfigs.set(phoneId, configs); for (Record r : mRecords) { if (r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED) @@ -2775,6 +2776,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mDataEnabledReason=" + mDataEnabledReason); pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]); pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]); + pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i)); pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i)); pw.decreaseIndent(); } @@ -2785,7 +2787,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.println("mDefaultPhoneId=" + mDefaultPhoneId); pw.println("mDefaultSubId=" + mDefaultSubId); - pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 3b31ab272ff2..039f4d9f3cce 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -362,8 +362,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { /** Notifies the VcnManagementService that external dependencies can be set up. */ public void systemReady() { - mContext.getSystemService(ConnectivityManager.class) - .registerNetworkProvider(mNetworkProvider); + mNetworkProvider.register(); mContext.getSystemService(ConnectivityManager.class) .registerNetworkCallback( new NetworkRequest.Builder().clearCapabilities().build(), @@ -935,13 +934,31 @@ public class VcnManagementService extends IVcnManagementService.Stub { pw.println("VcnManagementService dump:"); pw.increaseIndent(); + pw.println("mNetworkProvider:"); + pw.increaseIndent(); mNetworkProvider.dump(pw); + pw.decreaseIndent(); + pw.println(); + + pw.println("mTrackingNetworkCallback:"); + pw.increaseIndent(); + mTrackingNetworkCallback.dump(pw); + pw.decreaseIndent(); + pw.println(); synchronized (mLock) { + pw.println("mLastSnapshot:"); + pw.increaseIndent(); + mLastSnapshot.dump(pw); + pw.decreaseIndent(); + pw.println(); + pw.println("mVcns:"); + pw.increaseIndent(); for (Vcn vcn : mVcns.values()) { vcn.dump(pw); } + pw.decreaseIndent(); pw.println(); } @@ -1003,6 +1020,24 @@ public class VcnManagementService extends IVcnManagementService.Stub { return false; } + + /** Dumps the state of this snapshot for logging and debugging purposes. */ + public void dump(IndentingPrintWriter pw) { + pw.println("TrackingNetworkCallback:"); + pw.increaseIndent(); + + pw.println("mCaps:"); + pw.increaseIndent(); + synchronized (mCaps) { + for (Entry<Network, NetworkCapabilities> entry : mCaps.entrySet()) { + pw.println(entry.getKey() + ": " + entry.getValue()); + } + } + pw.decreaseIndent(); + pw.println(); + + pw.decreaseIndent(); + } } /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */ diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2a1a8971ec8c..e3b06d696423 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2500,7 +2500,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void batterySendBroadcast(Intent intent) { synchronized (this) { - broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, + broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } @@ -3940,7 +3940,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_UID, uid); intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, OP_NONE, + null, null, 0, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.getUserId(uid)); } @@ -7531,7 +7531,7 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, null, intent, - null, null, 0, null, null, null, OP_NONE, + null, null, 0, null, null, null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); @@ -7543,8 +7543,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {} - }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, OP_NONE, null, - true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, UserHandle.USER_ALL); } catch (Throwable e) { Slog.wtf(TAG, "Failed sending first user broadcasts", e); @@ -12302,7 +12302,7 @@ public class ActivityManagerService extends IActivityManager.Stub Intent intent = allSticky.get(i); BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, - null, null, -1, -1, false, null, null, OP_NONE, null, receivers, + null, null, -1, -1, false, null, null, null, OP_NONE, null, receivers, null, 0, null, null, false, true, true, -1, false, null, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */); queue.enqueueParallelBroadcastLocked(r); @@ -12544,13 +12544,13 @@ public class ActivityManagerService extends IActivityManager.Stub final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, String callerFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, - Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, - int realCallingPid, int userId) { + Bundle resultExtras, String[] requiredPermissions, String[] excludedPermissions, + int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, + int callingUid, int realCallingUid, int realCallingPid, int userId) { return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, - appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, - realCallingPid, userId, false /* allowBackgroundActivityStarts */, + excludedPermissions, appOp, bOptions, ordered, sticky, callingPid, callingUid, + realCallingUid, realCallingPid, userId, false /* allowBackgroundActivityStarts */, null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastAllowList */); } @@ -12558,9 +12558,11 @@ public class ActivityManagerService extends IActivityManager.Stub final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, @Nullable String callerFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, - Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, - int realCallingPid, int userId, boolean allowBackgroundActivityStarts, + Bundle resultExtras, String[] requiredPermissions, + String[] excludedPermissions, int appOp, Bundle bOptions, + boolean ordered, boolean sticky, int callingPid, int callingUid, + int realCallingUid, int realCallingPid, int userId, + boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastAllowList) { intent = new Intent(intent); @@ -13139,8 +13141,8 @@ public class ActivityManagerService extends IActivityManager.Stub final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, - resultCode, resultData, resultExtras, ordered, sticky, false, userId, + requiredPermissions, excludedPermissions, appOp, brOptions, registeredReceivers, + resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, allowBackgroundActivityStarts, backgroundActivityStartsToken, timeoutExempt); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); @@ -13237,10 +13239,10 @@ public class ActivityManagerService extends IActivityManager.Stub BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, - resultData, resultExtras, ordered, sticky, false, userId, - allowBackgroundActivityStarts, backgroundActivityStartsToken, - timeoutExempt); + requiredPermissions, excludedPermissions, appOp, brOptions, + receivers, resultTo, resultCode, resultData, resultExtras, + ordered, sticky, false, userId, allowBackgroundActivityStarts, + backgroundActivityStartsToken, timeoutExempt); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); @@ -13366,14 +13368,14 @@ public class ActivityManagerService extends IActivityManager.Stub String[] requiredPermissions, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { return broadcastIntentWithFeature(caller, null, intent, resolvedType, resultTo, resultCode, - resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky, - userId); + resultData, resultExtras, requiredPermissions, null, appOp, bOptions, serialized, + sticky, userId); } public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, - String[] requiredPermissions, int appOp, Bundle bOptions, + String[] requiredPermissions, String[] excludedPermissions, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) { enforceNotIsolatedCaller("broadcastIntent"); synchronized(this) { @@ -13388,8 +13390,8 @@ public class ActivityManagerService extends IActivityManager.Stub return broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, callingFeatureId, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, - requiredPermissions, appOp, bOptions, serialized, sticky, - callingPid, callingUid, callingUid, callingPid, userId); + requiredPermissions, excludedPermissions, appOp, bOptions, serialized, + sticky, callingPid, callingUid, callingUid, callingPid, userId); } finally { Binder.restoreCallingIdentity(origId); } @@ -13410,7 +13412,7 @@ public class ActivityManagerService extends IActivityManager.Stub : new String[] {requiredPermission}; try { return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType, - resultTo, resultCode, resultData, resultExtras, requiredPermissions, + resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid, realCallingPid, userId, allowBackgroundActivityStarts, backgroundActivityStartsToken, @@ -15615,7 +15617,7 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/, null /*callerPackage*/, null /*callingFeatureId*/, intent, null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/, - null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE, + null /*resultExtras*/, requiredPermissions, null, AppOpsManager.OP_NONE, bOptions /*options*/, serialized, false /*sticky*/, callingPid, callingUid, callingUid, callingPid, userId, false /*allowBackgroundStarts*/, @@ -15740,8 +15742,8 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), UserHandle.USER_ALL); + null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND @@ -15751,8 +15753,9 @@ public class ActivityManagerService extends IActivityManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), - Binder.getCallingPid(), UserHandle.USER_ALL); + null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), + UserHandle.USER_ALL); } // Send a broadcast to PackageInstallers if the configuration change is interesting @@ -15766,7 +15769,7 @@ public class ActivityManagerService extends IActivityManager.Stub String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES }; broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, - permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + permissions, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -15791,7 +15794,7 @@ public class ActivityManagerService extends IActivityManager.Stub } broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + null, OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 42aac295c027..6fa8ecd41d7c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -760,7 +760,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.flush(); Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle(); mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null, - requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false, + requiredPermissions, null, android.app.AppOpsManager.OP_NONE, bundle, true, false, mUserId); if (!mAsync) { receiver.waitForFinish(); diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 60e8d54676ec..4942b110cf3c 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -205,7 +205,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mPowerStatsInternal = psi; boolean[] supportedStdBuckets = null; - int numCustomBuckets = 0; + String[] customBucketNames = null; if (mPowerStatsInternal != null) { final SparseArray<EnergyConsumer> idToConsumer = populateEnergyConsumerSubsystemMapsLocked(); @@ -227,12 +227,12 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { + e.getCause()); // Continue running, later attempts to query may be successful. } - numCustomBuckets = mMeasuredEnergySnapshot.getNumOtherOrdinals(); + customBucketNames = mMeasuredEnergySnapshot.getOtherOrdinalNames(); supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer); } } synchronized (mStats) { - mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, numCustomBuckets); + mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, customBucketNames); } } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index a5474d0b8c72..f0b116cc1869 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -1419,6 +1419,7 @@ public final class BroadcastQueue { skip = true; } } + boolean isSingleton = false; try { isSingleton = mService.isSingleton(info.activityInfo.processName, @@ -1553,6 +1554,37 @@ public final class BroadcastQueue { + info.activityInfo.applicationInfo.uid + " : user is not running"); } + if (!skip && r.excludedPermissions != null && r.excludedPermissions.length > 0) { + for (int i = 0; i < r.excludedPermissions.length; i++) { + String excludedPermission = r.excludedPermissions[i]; + try { + perm = AppGlobals.getPackageManager() + .checkPermission(excludedPermission, + info.activityInfo.applicationInfo.packageName, + UserHandle + .getUserId(info.activityInfo.applicationInfo.uid)); + } catch (RemoteException e) { + perm = PackageManager.PERMISSION_DENIED; + } + + if (perm == PackageManager.PERMISSION_GRANTED) { + skip = true; + break; + } + + int appOp = AppOpsManager.permissionToOpCode(excludedPermission); + if (appOp != AppOpsManager.OP_NONE) { + if (mService.getAppOpsManager().checkOpNoThrow(appOp, + info.activityInfo.applicationInfo.uid, + info.activityInfo.packageName) + == AppOpsManager.MODE_ALLOWED) { + skip = true; + break; + } + } + } + } + if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && r.requiredPermissions != null && r.requiredPermissions.length > 0) { for (int i = 0; i < r.requiredPermissions.length; i++) { diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 198ba34e3956..801559620457 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -62,6 +62,7 @@ final class BroadcastRecord extends Binder { final int userId; // user id this broadcast was for final String resolvedType; // the resolved data type final String[] requiredPermissions; // permissions the caller has required + final String[] excludedPermissions; // permissions to exclude final int appOp; // an app op that is associated with this broadcast final BroadcastOptions options; // BroadcastOptions supplied by caller final List receivers; // contains BroadcastFilter and ResolveInfo @@ -142,6 +143,10 @@ final class BroadcastRecord extends Binder { pw.print(Arrays.toString(requiredPermissions)); pw.print(" appOp="); pw.println(appOp); } + if (excludedPermissions != null && excludedPermissions.length > 0) { + pw.print(prefix); pw.print("excludedPermissions="); + pw.print(Arrays.toString(excludedPermissions)); + } if (options != null) { pw.print(prefix); pw.print("options="); pw.println(options.toBundle()); } @@ -240,11 +245,11 @@ final class BroadcastRecord extends Binder { Intent _intent, ProcessRecord _callerApp, String _callerPackage, @Nullable String _callerFeatureId, int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType, - String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers, - IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, - boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId, - boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken, - boolean timeoutExempt) { + String[] _requiredPermissions, String[] _excludedPermissions, int _appOp, + BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode, + String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky, + boolean _initialSticky, int _userId, boolean allowBackgroundActivityStarts, + @Nullable IBinder backgroundActivityStartsToken, boolean timeoutExempt) { if (_intent == null) { throw new NullPointerException("Can't construct with a null intent"); } @@ -259,6 +264,7 @@ final class BroadcastRecord extends Binder { callerInstantApp = _callerInstantApp; resolvedType = _resolvedType; requiredPermissions = _requiredPermissions; + excludedPermissions = _excludedPermissions; appOp = _appOp; options = _options; receivers = _receivers; @@ -299,6 +305,7 @@ final class BroadcastRecord extends Binder { userId = from.userId; resolvedType = from.resolvedType; requiredPermissions = from.requiredPermissions; + excludedPermissions = from.excludedPermissions; appOp = from.appOp; options = from.options; receivers = from.receivers; @@ -356,8 +363,8 @@ final class BroadcastRecord extends Binder { // build a new BroadcastRecord around that single-target list BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, - requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode, - resultData, resultExtras, ordered, sticky, initialSticky, userId, + requiredPermissions, excludedPermissions, appOp, options, splitReceivers, resultTo, + resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt); split.splitToken = this.splitToken; diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java index f9296a0e64fc..65d47550a2ed 100644 --- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java +++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java @@ -99,14 +99,6 @@ public class MeasuredEnergySnapshot { mAttributionSnapshots = new SparseArray<>(mNumOtherOrdinals); } - /** - * Returns the number of ordinals for {@link EnergyConsumerType#OTHER}, i.e. the number of - * custom energy buckets supported by the device. - */ - public int getNumOtherOrdinals() { - return mNumOtherOrdinals; - } - /** Class for returning the relevant data calculated from the measured energy delta */ static class MeasuredEnergyDeltaData { /** The chargeUC for {@link EnergyConsumerType#BLUETOOTH}. */ @@ -147,7 +139,7 @@ public class MeasuredEnergySnapshot { * Fields with no interesting data (consumers not present in ecrs or with no energy * difference) will generally be left as their default values. * otherTotalChargeUC and otherUidChargesUC are always either both null or both of - * length {@link #getNumOtherOrdinals()}. + * length {@link #getOtherOrdinalNames().length}. * Returns null, if ecrs is null or empty. */ public @Nullable MeasuredEnergyDeltaData updateAndGetDelta(EnergyConsumerResult[] ecrs, @@ -237,8 +229,8 @@ public class MeasuredEnergySnapshot { case EnergyConsumerType.OTHER: if (output.otherTotalChargeUC == null) { - output.otherTotalChargeUC = new long[getNumOtherOrdinals()]; - output.otherUidChargesUC = new SparseLongArray[getNumOtherOrdinals()]; + output.otherTotalChargeUC = new long[mNumOtherOrdinals]; + output.otherUidChargesUC = new SparseLongArray[mNumOtherOrdinals]; } output.otherTotalChargeUC[ordinal] = deltaChargeUC; output.otherUidChargesUC[ordinal] = otherUidCharges; @@ -342,6 +334,23 @@ public class MeasuredEnergySnapshot { pw.println(); } + /** + * Returns the names of ordinals for {@link EnergyConsumerType#OTHER}, i.e. the names of + * custom energy buckets supported by the device. + */ + public String[] getOtherOrdinalNames() { + final String[] names = new String[mNumOtherOrdinals]; + int consumerIndex = 0; + final int size = mEnergyConsumers.size(); + for (int idx = 0; idx < size; idx++) { + final EnergyConsumer consumer = mEnergyConsumers.valueAt(idx); + if (consumer.type == (int) EnergyConsumerType.OTHER) { + names[consumerIndex++] = consumer.name; + } + } + return names; + } + /** Determines the number of ordinals for a given {@link EnergyConsumerType}. */ private static int calculateNumOrdinals(@EnergyConsumerType int type, SparseArray<EnergyConsumer> idToConsumer) { @@ -361,5 +370,4 @@ public class MeasuredEnergySnapshot { // since the last snapshot. Round off to the nearest whole long. return (deltaEnergyUJ * MILLIVOLTS_PER_VOLT + (avgVoltageMV / 2)) / avgVoltageMV; } - } diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 984fe409b086..756209824614 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -124,7 +124,7 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { REASON_PRE_BOOT_COMPLETED, ""); synchronized (mService) { mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null, - null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, + null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true, false, ActivityManagerService.MY_PID, Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId); } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index a5d0e72a81ae..ba3e1fb95e7d 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2936,8 +2936,8 @@ class UserController implements Handler.Callback { // TODO b/64165549 Verify that mLock is not held before calling AMS methods synchronized (mService) { return mService.broadcastIntentLocked(null, null, null, intent, resolvedType, - resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, - bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, + resultTo, resultCode, resultData, resultExtras, requiredPermissions, null, + appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId); } } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 419e686e237d..3f075724662f 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -1827,7 +1827,8 @@ public class AppOpsService extends IAppOpsService.Stub { } } - mHistoricalRegistry.clearHistory(uid, packageName); + mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory, + mHistoricalRegistry, uid, packageName)); } public void uidRemoved(int uid) { diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index cb7c568757e5..a546a60e20ef 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -35,7 +35,6 @@ import android.database.ContentObserver; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricPrompt; -import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -51,6 +50,7 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; @@ -338,18 +338,31 @@ public class BiometricService extends SystemService { private static final boolean DEFAULT_APP_ENABLED = true; private static final boolean DEFAULT_ALWAYS_REQUIRE_CONFIRMATION = false; + // Some devices that shipped before S already have face-specific settings. Instead of + // migrating, which is complicated, let's just keep using the existing settings. + private final boolean mUseLegacyFaceOnlySettings; + + // Only used for legacy face-only devices private final Uri FACE_UNLOCK_KEYGUARD_ENABLED = Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED); private final Uri FACE_UNLOCK_APP_ENABLED = Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED); + + // Continues to be used, even though it's face-specific. private final Uri FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION = Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION); + // Used for all devices other than legacy face-only devices + private final Uri BIOMETRIC_KEYGUARD_ENABLED = + Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED); + private final Uri BIOMETRIC_APP_ENABLED = + Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED); + private final ContentResolver mContentResolver; private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks; - private final Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>(); - private final Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>(); + private final Map<Integer, Boolean> mBiometricEnabledOnKeyguard = new HashMap<>(); + private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>(); private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>(); /** @@ -362,21 +375,44 @@ public class BiometricService extends SystemService { super(handler); mContentResolver = context.getContentResolver(); mCallbacks = callbacks; + + final boolean hasFingerprint = context.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); + final boolean hasFace = context.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_FACE); + + // Use the legacy setting on face-only devices that shipped on or before Q + mUseLegacyFaceOnlySettings = + Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q + && hasFace && !hasFingerprint; + updateContentObserver(); } public void updateContentObserver() { mContentResolver.unregisterContentObserver(this); - mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED, - false /* notifyForDescendents */, - this /* observer */, - UserHandle.USER_ALL); - mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED, - false /* notifyForDescendents */, - this /* observer */, - UserHandle.USER_ALL); + + if (mUseLegacyFaceOnlySettings) { + mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + } else { + mContentResolver.registerContentObserver(BIOMETRIC_KEYGUARD_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + mContentResolver.registerContentObserver(BIOMETRIC_APP_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + } mContentResolver.registerContentObserver(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, - false /* notifyForDescendents */, + false /* notifyForDescendants */, this /* observer */, UserHandle.USER_ALL); } @@ -384,7 +420,7 @@ public class BiometricService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri, int userId) { if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) { - mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( + mBiometricEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( mContentResolver, Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, @@ -394,7 +430,7 @@ public class BiometricService extends SystemService { notifyEnabledOnKeyguardCallbacks(userId); } } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) { - mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser( + mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser( mContentResolver, Settings.Secure.FACE_UNLOCK_APP_ENABLED, DEFAULT_APP_ENABLED ? 1 : 0 /* default */, @@ -405,22 +441,45 @@ public class BiometricService extends SystemService { Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, DEFAULT_ALWAYS_REQUIRE_CONFIRMATION ? 1 : 0 /* default */, userId) != 0); + } else if (BIOMETRIC_KEYGUARD_ENABLED.equals(uri)) { + mBiometricEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( + mContentResolver, + Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED, + DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, + userId) != 0); + + if (userId == ActivityManager.getCurrentUser() && !selfChange) { + notifyEnabledOnKeyguardCallbacks(userId); + } + } else if (BIOMETRIC_APP_ENABLED.equals(uri)) { + mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser( + mContentResolver, + Settings.Secure.BIOMETRIC_APP_ENABLED, + DEFAULT_APP_ENABLED ? 1 : 0 /* default */, + userId) != 0); } } - public boolean getFaceEnabledOnKeyguard() { - final int user = ActivityManager.getCurrentUser(); - if (!mFaceEnabledOnKeyguard.containsKey(user)) { - onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user); + public boolean getEnabledOnKeyguard(int userId) { + if (!mBiometricEnabledOnKeyguard.containsKey(userId)) { + if (mUseLegacyFaceOnlySettings) { + onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, userId); + } else { + onChange(true /* selfChange */, BIOMETRIC_KEYGUARD_ENABLED, userId); + } } - return mFaceEnabledOnKeyguard.get(user); + return mBiometricEnabledOnKeyguard.get(userId); } - public boolean getFaceEnabledForApps(int userId) { - if (!mFaceEnabledForApps.containsKey(userId)) { - onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); + public boolean getEnabledForApps(int userId) { + if (!mBiometricEnabledForApps.containsKey(userId)) { + if (mUseLegacyFaceOnlySettings) { + onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); + } else { + onChange(true /* selfChange */, BIOMETRIC_APP_ENABLED, userId); + } } - return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED); + return mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED); } public boolean getConfirmationAlwaysRequired(@BiometricAuthenticator.Modality int modality, @@ -442,8 +501,8 @@ public class BiometricService extends SystemService { void notifyEnabledOnKeyguardCallbacks(int userId) { List<EnabledOnKeyguardCallback> callbacks = mCallbacks; for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).notify(BiometricSourceType.FACE, - mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED), + callbacks.get(i).notify( + mBiometricEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED), userId); } } @@ -462,9 +521,9 @@ public class BiometricService extends SystemService { } } - void notify(BiometricSourceType sourceType, boolean enabled, int userId) { + void notify(boolean enabled, int userId) { try { - mCallback.onChanged(sourceType, enabled, userId); + mCallback.onChanged(enabled, userId); } catch (DeadObjectException e) { Slog.w(TAG, "Death while invoking notify", e); mEnabledOnKeyguardCallbacks.remove(this); @@ -747,8 +806,8 @@ public class BiometricService extends SystemService { mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback)); try { - callback.onChanged(BiometricSourceType.FACE, - mSettingObserver.getFaceEnabledOnKeyguard(), callingUserId); + callback.onChanged(mSettingObserver.getEnabledOnKeyguard(callingUserId), + callingUserId); } catch (RemoteException e) { Slog.w(TAG, "Remote exception", e); } @@ -1347,6 +1406,9 @@ public class BiometricService extends SystemService { } private void dumpInternal(PrintWriter pw) { + pw.println("Legacy Settings: " + mSettingObserver.mUseLegacyFaceOnlySettings); + pw.println(); + pw.println("Sensors:"); for (BiometricSensor sensor : mSensors) { pw.println(" " + sensor); diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 262cb36d7bdb..c4bd18b59745 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -197,17 +197,7 @@ class PreAuthInfo { private static boolean isEnabledForApp(BiometricService.SettingObserver settingObserver, @BiometricAuthenticator.Modality int modality, int userId) { - switch (modality) { - case TYPE_FINGERPRINT: - return true; - case TYPE_IRIS: - return true; - case TYPE_FACE: - return settingObserver.getFaceEnabledForApps(userId); - default: - Slog.w(TAG, "Unsupported modality: " + modality); - return false; - } + return settingObserver.getEnabledForApps(userId); } private static boolean isBiometricDisabledByDevicePolicy( diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 1bbcedeb0494..b2d35f45ab44 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -15,9 +15,6 @@ */ package com.android.server.camera; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.os.Build.VERSION_CODES.M; import android.annotation.IntDef; @@ -27,15 +24,12 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; 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.res.Configuration; -import android.graphics.Rect; import android.hardware.CameraSessionStats; import android.hardware.CameraStreamStats; import android.hardware.ICameraService; @@ -43,7 +37,6 @@ import android.hardware.ICameraServiceProxy; import android.hardware.camera2.CameraMetadata; import android.hardware.display.DisplayManager; import android.media.AudioManager; -import android.metrics.LogMaker; import android.nfc.INfcAdapter; import android.os.Binder; import android.os.Handler; @@ -60,17 +53,16 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.view.Display; +import android.view.IDisplayWindowListener; import android.view.Surface; +import android.view.WindowManagerGlobal; import com.android.internal.annotations.GuardedBy; import com.android.framework.protobuf.nano.MessageNano; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.wm.WindowManagerInternal; import java.lang.annotation.Retention; @@ -223,6 +215,37 @@ public class CameraServiceProxy extends SystemService } } + private final class DisplayWindowListener extends IDisplayWindowListener.Stub { + + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + ICameraService cs = getCameraServiceRawLocked(); + if (cs == null) return; + + try { + cs.notifyDisplayConfigurationChange(); + } catch (RemoteException e) { + Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e); + // Not much we can do if camera service is dead. + } + } + + @Override + public void onDisplayAdded(int displayId) { } + + @Override + public void onDisplayRemoved(int displayId) { } + + @Override + public void onFixedRotationStarted(int displayId, int newRotation) { } + + @Override + public void onFixedRotationFinished(int displayId) { } + } + + + private final DisplayWindowListener mDisplayWindowListener = new DisplayWindowListener(); + private final TaskStateHandler mTaskStackListener = new TaskStateHandler(); private final class TaskInfo { @@ -236,13 +259,12 @@ public class CameraServiceProxy extends SystemService private final class TaskStateHandler extends TaskStackListener { private final Object mMapLock = new Object(); - // maps the current top level task id to its corresponding package name + // maps the package name to its corresponding current top level task id @GuardedBy("mMapLock") private final ArrayMap<String, TaskInfo> mTaskInfoMap = new ArrayMap<>(); @Override - public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) - throws RemoteException { + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { synchronized (mMapLock) { TaskInfo info = new TaskInfo(); info.frontTaskId = taskInfo.taskId; @@ -257,7 +279,7 @@ public class CameraServiceProxy extends SystemService } @Override - public void onTaskRemoved(int taskId) throws RemoteException { + public void onTaskRemoved(int taskId) { synchronized (mMapLock) { for (Map.Entry<String, TaskInfo> entry : mTaskInfoMap.entrySet()){ if (entry.getValue().frontTaskId == taskId) { @@ -319,7 +341,7 @@ public class CameraServiceProxy extends SystemService /** * Gets whether crop-rotate-scale is needed. */ - private boolean getNeedCropRotateScale(Context ctx, String packageName, + private boolean getNeedCropRotateScale(@NonNull Context ctx, @NonNull String packageName, @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) { if (taskInfo == null) { return false; @@ -334,7 +356,7 @@ public class CameraServiceProxy extends SystemService // Only enable the crop-rotate-scale workaround if the app targets M or below and is not // resizeable. - if ((ctx != null) && !isMOrBelow(ctx, packageName) && taskInfo.isResizeable) { + if (!isMOrBelow(ctx, packageName) && taskInfo.isResizeable) { Slog.v(TAG, "The activity is N or above and claims to support resizeable-activity. " + "Crop-rotate-scale is disabled."); @@ -372,12 +394,8 @@ public class CameraServiceProxy extends SystemService taskInfo.isFixedOrientationLandscape); // We need to do crop-rotate-scale when camera is landscape and activity is portrait or // vice versa. - if ((taskInfo.isFixedOrientationPortrait && landscapeCamera) - || (taskInfo.isFixedOrientationLandscape && !landscapeCamera)) { - return true; - } else { - return false; - } + return (taskInfo.isFixedOrientationPortrait && landscapeCamera) + || (taskInfo.isFixedOrientationLandscape && !landscapeCamera); } @Override @@ -389,14 +407,10 @@ public class CameraServiceProxy extends SystemService return false; } - // A few remaining todos: - // 1) Do the same check when working in WM compatible mode. The sequence needs - // to be adjusted and use orientation events as triggers for all active camera - // clients. - // 2) Modify the sensor orientation in camera characteristics along with any 3A regions - // in capture requests/results to account for thea physical rotation. The former - // is somewhat tricky as it assumes that camera clients always check for the current - // value by retrieving the camera characteristics from the camera device. + // TODO: Modify the sensor orientation in camera characteristics along with any 3A + // regions in capture requests/results to account for thea physical rotation. The + // former is somewhat tricky as it assumes that camera clients always check for the + // current value by retrieving the camera characteristics from the camera device. return getNeedCropRotateScale(mContext, packageName, mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation, lensFacing); @@ -522,18 +536,25 @@ public class CameraServiceProxy extends SystemService publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy); publishLocalService(CameraServiceProxy.class, this); - - try { - ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Log.e(TAG, "Failed to register task stack listener!"); - } } @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { CameraStatsJobService.schedule(mContext); + + try { + ActivityTaskManager.getService().registerTaskStackListener(mTaskStackListener); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register task stack listener!"); + } + + try { + WindowManagerGlobal.getWindowManagerService().registerDisplayWindowListener( + mDisplayWindowListener); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register display window listener!"); + } } } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 9c10814c7591..47c7e3902556 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -45,6 +45,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -67,6 +68,7 @@ import android.view.autofill.AutofillManagerInternal; import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; +import android.view.textclassifier.TextClassifierEvent; import android.view.textclassifier.TextLinks; import android.widget.Toast; @@ -211,9 +213,18 @@ public class ClipboardService extends SystemService { /** Uids that have already triggered a toast notification for {@link #primaryClip} */ final SparseBooleanArray mNotifiedUids = new SparseBooleanArray(); + /** + * Uids that have already triggered a notification to text classifier for + * {@link #primaryClip}. + */ + final SparseBooleanArray mNotifiedTextClassifierUids = new SparseBooleanArray(); + final HashSet<String> activePermissionOwners = new HashSet<String>(); + /** The text classifier session that is used to annotate the text in the primary clip. */ + TextClassifier mTextClassifier; + PerUserClipboard(int userId) { this.userId = userId; } @@ -371,6 +382,7 @@ public class ClipboardService extends SystemService { addActiveOwnerLocked(intendingUid, pkg); PerUserClipboard clipboard = getClipboardLocked(intendingUserId); showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard); + notifyTextClassifierLocked(clipboard, pkg, intendingUid); return clipboard.primaryClip; } } @@ -580,6 +592,7 @@ public class ClipboardService extends SystemService { } clipboard.primaryClip = clip; clipboard.mNotifiedUids.clear(); + clipboard.mNotifiedTextClassifierUids.clear(); if (clip != null) { clipboard.primaryClipUid = uid; clipboard.mPrimaryClipPackage = sourcePackage; @@ -619,6 +632,11 @@ public class ClipboardService extends SystemService { @GuardedBy("mLock") private void startClassificationLocked(@NonNull ClipData clip, @UserIdInt int userId) { + if (clip.getItemCount() == 0) { + clip.getDescription().setClassificationStatus( + ClipDescription.CLASSIFICATION_NOT_PERFORMED); + return; + } TextClassifier classifier; final long ident = Binder.clearCallingIdentity(); try { @@ -627,17 +645,11 @@ public class ClipboardService extends SystemService { new TextClassificationContext.Builder( getContext().getPackageName(), TextClassifier.WIDGET_TYPE_CLIPBOARD - ).build() - ); + ).build() + ); } finally { Binder.restoreCallingIdentity(ident); } - - if (clip.getItemCount() == 0) { - clip.getDescription().setClassificationStatus( - ClipDescription.CLASSIFICATION_NOT_PERFORMED); - return; - } CharSequence text = clip.getItemAt(0).getText(); if (TextUtils.isEmpty(text) || text.length() > mMaxClassificationLength || text.length() > classifier.getMaxGenerateLinksTextLength()) { @@ -645,7 +657,7 @@ public class ClipboardService extends SystemService { ClipDescription.CLASSIFICATION_NOT_PERFORMED); return; } - + getClipboardLocked(userId).mTextClassifier = classifier; mWorkerHandler.post(() -> doClassification(text, clip, classifier)); } @@ -653,12 +665,7 @@ public class ClipboardService extends SystemService { private void doClassification( CharSequence text, ClipData clip, TextClassifier classifier) { TextLinks.Request request = new TextLinks.Request.Builder(text).build(); - TextLinks links; - try { - links = classifier.generateLinks(request); - } finally { - classifier.destroy(); - } + TextLinks links = classifier.generateLinks(request); // Find the highest confidence for each entity in the text. ArrayMap<String, Float> confidences = new ArrayMap<>(); @@ -979,6 +986,48 @@ public class ClipboardService extends SystemService { && item.getIntent() == null; } + /** Potentially notifies the text classifier that an app is accessing a text clip. */ + @GuardedBy("mLock") + private void notifyTextClassifierLocked( + PerUserClipboard clipboard, String callingPackage, int callingUid) { + if (clipboard.primaryClip == null) { + return; + } + ClipData.Item item = clipboard.primaryClip.getItemAt(0); + if (item == null) { + return; + } + if (!isText(clipboard.primaryClip)) { + return; + } + TextClassifier textClassifier = clipboard.mTextClassifier; + // Don't notify text classifier if we haven't used it to annotate the text in the clip. + if (textClassifier == null) { + return; + } + // Don't notify text classifier if the app reading the clipboard does not have the focus. + if (!mWm.isUidFocused(callingUid)) { + return; + } + // Don't notify text classifier again if already notified for this uid and clip. + if (clipboard.mNotifiedTextClassifierUids.get(callingUid)) { + return; + } + clipboard.mNotifiedTextClassifierUids.put(callingUid, true); + Binder.withCleanCallingIdentity(() -> { + TextClassifierEvent.TextLinkifyEvent pasteEvent = + new TextClassifierEvent.TextLinkifyEvent.Builder( + TextClassifierEvent.TYPE_READ_CLIPBOARD) + .setEventContext(new TextClassificationContext.Builder( + callingPackage, TextClassifier.WIDGET_TYPE_CLIPBOARD) + .build()) + .setExtras( + Bundle.forPair("source_package", clipboard.mPrimaryClipPackage)) + .build(); + textClassifier.onTextClassifierEvent(pasteEvent); + }); + } + private TextClassificationManager createTextClassificationManagerAsUser(@UserIdInt int userId) { Context context = getContext().createContextAsUser(UserHandle.of(userId), /* flags= */ 0); return context.getSystemService(TextClassificationManager.class); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 4d310cbc06bb..584174e237a3 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -59,6 +59,7 @@ import com.android.internal.util.WakeupMessage; import com.android.server.ConnectivityService; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; @@ -281,6 +282,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa */ public static final int ARG_AGENT_SUCCESS = 1; + // How long this network should linger for. + private int mLingerDurationMs; + // All inactivity timers for this network, sorted by expiry time. A timer is added whenever // a request is moved to a network with a better score, regardless of whether the network is or // was lingering or not. An inactivity timer is also added when a network connects @@ -349,7 +353,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa @NonNull NetworkScore score, Context context, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid, - QosCallbackTracker qosCallbackTracker, ConnectivityService.Dependencies deps) { + int lingerDurationMs, QosCallbackTracker qosCallbackTracker, + ConnectivityService.Dependencies deps) { Objects.requireNonNull(net); Objects.requireNonNull(info); Objects.requireNonNull(lp); @@ -370,6 +375,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa mHandler = handler; this.factorySerialNumber = factorySerialNumber; this.creatorUid = creatorUid; + mLingerDurationMs = lingerDurationMs; mQosCallbackTracker = qosCallbackTracker; } @@ -685,6 +691,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa mHandler.obtainMessage(NetworkAgent.EVENT_TEARDOWN_DELAY_CHANGED, teardownDelayMs, 0, new Pair<>(NetworkAgentInfo.this, null)).sendToTarget(); } + + @Override + public void sendLingerDuration(final int durationMs) { + mHandler.obtainMessage(NetworkAgent.EVENT_LINGER_DURATION_CHANGED, + new Pair<>(NetworkAgentInfo.this, durationMs)).sendToTarget(); + } } /** @@ -954,13 +966,14 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa /** * Sets the specified requestId to linger on this network for the specified time. Called by - * ConnectivityService when the request is moved to another network with a higher score, or + * ConnectivityService when any request is moved to another network with a higher score, or * when a network is newly created. * * @param requestId The requestId of the request that no longer need to be served by this * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the - * {@code LingerTimer} for a newly created network. + * {@code InactivityTimer} for a newly created network. */ + // TODO: Consider creating a dedicated function for nascent network, e.g. start/stopNascent. public void lingerRequest(int requestId, long now, long duration) { if (mInactivityTimerForRequest.get(requestId) != null) { // Cannot happen. Once a request is lingering on a particular network, we cannot @@ -976,6 +989,19 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa } /** + * Sets the specified requestId to linger on this network for the timeout set when + * initializing or modified by {@link #setLingerDuration(int)}. Called by + * ConnectivityService when any request is moved to another network with a higher score. + * + * @param requestId The requestId of the request that no longer need to be served by this + * network. + * @param now current system timestamp obtained by {@code SystemClock.elapsedRealtime}. + */ + public void lingerRequest(int requestId, long now) { + lingerRequest(requestId, now, mLingerDurationMs); + } + + /** * Cancel lingering. Called by ConnectivityService when a request is added to this network. * Returns true if the given requestId was lingering on this network, false otherwise. */ @@ -1012,6 +1038,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa } if (newExpiry > 0) { + // If the newExpiry timestamp is in the past, the wakeup message will fire immediately. mInactivityMessage = new WakeupMessage( mContext, mHandler, "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */, @@ -1041,8 +1068,33 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa } /** - * Return whether the network is just connected and about to be torn down because of not - * satisfying any request. + * Set the linger duration for this NAI. + * @param durationMs The new linger duration, in milliseconds. + */ + public void setLingerDuration(final int durationMs) { + final long diff = durationMs - mLingerDurationMs; + final ArrayList<InactivityTimer> newTimers = new ArrayList<>(); + for (final InactivityTimer timer : mInactivityTimers) { + if (timer.requestId == NetworkRequest.REQUEST_ID_NONE) { + // Don't touch nascent timer, re-add as is. + newTimers.add(timer); + } else { + newTimers.add(new InactivityTimer(timer.requestId, timer.expiryMs + diff)); + } + } + mInactivityTimers.clear(); + mInactivityTimers.addAll(newTimers); + updateInactivityTimer(); + mLingerDurationMs = durationMs; + } + + /** + * Return whether the network satisfies no request, but is still being kept up + * because it has just connected less than + * {@code ConnectivityService#DEFAULT_NASCENT_DELAY_MS}ms ago and is thus still considered + * nascent. Note that nascent mechanism uses inactivity timer which isn't + * associated with a request. Thus, use {@link NetworkRequest#REQUEST_ID_NONE} to identify it. + * */ public boolean isNascent() { return mInactive && mInactivityTimers.size() == 1 diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java index c123ea75c66c..2b345e5aa79f 100644 --- a/services/core/java/com/android/server/connectivity/NetworkRanker.java +++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java @@ -24,6 +24,7 @@ import static android.net.NetworkScore.POLICY_EXITING; import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY; import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI; +import static com.android.net.module.util.CollectionUtils.filter; import static com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED; import static com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED; import static com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD; @@ -66,18 +67,6 @@ public class NetworkRanker { public NetworkRanker() { } - // TODO : move to module utils CollectionUtils. - @NonNull private static <T> ArrayList<T> filter(@NonNull final Collection<T> source, - @NonNull final Predicate<T> test) { - final ArrayList<T> matches = new ArrayList<>(); - for (final T e : source) { - if (test.test(e)) { - matches.add(e); - } - } - return matches; - } - /** * Find the best network satisfying this request among the list of passed networks. */ diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index e3eeb6c41d9f..3a7220f7592f 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -394,7 +394,8 @@ public final class DreamManagerService extends SystemService { private void startDreamLocked(final ComponentName name, final boolean isTest, final boolean canDoze, final int userId) { - if (Objects.equals(mCurrentDreamName, name) + if (!mCurrentDreamIsWaking + && Objects.equals(mCurrentDreamName, name) && mCurrentDreamIsTest == isTest && mCurrentDreamCanDoze == canDoze && mCurrentDreamUserId == userId) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 031c057018ad..754fa25191b0 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1253,6 +1253,7 @@ public class HdmiControlService extends SystemService { void setAudioStatus(boolean mute, int volume) { if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated() + || !tv().isArcEstablished() // Don't update TV volume when SAM is on and ARC is off || getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index ed8ea186b1d4..7994fccbd650 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4100,13 +4100,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void removeImeSurfaceFromWindow(IBinder windowToken, - IVoidResultCallback resultCallback) { - CallbackUtils.onResult(resultCallback, () -> { - // No permission check, because we'll only execute the request if the calling window is - // also the current IME client. - mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget(); - }); + public void removeImeSurfaceFromWindowAsync(IBinder windowToken) { + // No permission check, because we'll only execute the request if the calling window is + // also the current IME client. + mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget(); } /** diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index ef1489b4adf9..62447439003b 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1510,10 +1510,8 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public void removeImeSurfaceFromWindow(IBinder windowToken, - IVoidResultCallback resultCallback) { + public void removeImeSurfaceFromWindowAsync(IBinder windowToken) { reportNotSupported(); - CallbackUtils.onResult(resultCallback, () -> { }); } @BinderThread diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 6b28fbc9b86c..59f00a2a4bc7 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1773,7 +1773,7 @@ public class LockSettingsService extends ILockSettings.Stub { } else { final byte[] hashFactor = getHashFactor(password, userHandle); final byte[] salt = getSalt(userHandle).getBytes(); - String hash = password.passwordToHistoryHash(hashFactor, salt); + String hash = password.passwordToHistoryHash(salt, hashFactor); if (hash == null) { Slog.e(TAG, "Compute new style password hash failed, fallback to legacy style"); hash = password.legacyPasswordToHash(salt); @@ -3658,7 +3658,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public boolean armRebootEscrow() { + public @ArmRebootEscrowErrorCode int armRebootEscrow() { return mRebootEscrowManager.armRebootEscrowIfNeeded(); } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 76ecc1acc7ac..c01523a4bc40 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -18,6 +18,15 @@ package com.android.server.locksettings; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_STORE_ESCROW_KEY; +import static com.android.internal.widget.LockSettingsInternal.ArmRebootEscrowErrorCode; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -577,16 +586,14 @@ class RebootEscrowManager { mRebootEscrowWanted = false; setRebootEscrowReady(false); - RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider(); if (rebootEscrowProvider == null) { - Slog.w(TAG, - "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); - return; + Slog.w(TAG, "RebootEscrowProvider is unavailable for clear request"); + } else { + rebootEscrowProvider.clearRebootEscrowKey(); } clearMetricsStorage(); - rebootEscrowProvider.clearRebootEscrowKey(); List<UserInfo> users = mUserManager.getUsers(); for (UserInfo user : users) { @@ -596,20 +603,30 @@ class RebootEscrowManager { mEventLog.addEntry(RebootEscrowEvent.CLEARED_LSKF_REQUEST); } - boolean armRebootEscrowIfNeeded() { + @ArmRebootEscrowErrorCode int armRebootEscrowIfNeeded() { if (!mRebootEscrowReady) { - return false; + return ARM_REBOOT_ERROR_ESCROW_NOT_READY; } RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider(); if (rebootEscrowProvider == null) { Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrowProvider is unavailable"); - return false; + clearRebootEscrowIfNeeded(); + return ARM_REBOOT_ERROR_NO_PROVIDER; } + int expectedProviderType = mInjector.serverBasedResumeOnReboot() + ? RebootEscrowProviderInterface.TYPE_SERVER_BASED + : RebootEscrowProviderInterface.TYPE_HAL; int actualProviderType = rebootEscrowProvider.getType(); - // TODO(b/183140900) Fail the reboot if provider type mismatches. + if (expectedProviderType != actualProviderType) { + Slog.w(TAG, "Expect reboot escrow provider " + expectedProviderType + + ", but the RoR is prepared with " + actualProviderType + + ". Please prepare the RoR again."); + clearRebootEscrowIfNeeded(); + return ARM_REBOOT_ERROR_PROVIDER_MISMATCH; + } RebootEscrowKey escrowKey; synchronized (mKeyGenerationLock) { @@ -618,30 +635,38 @@ class RebootEscrowManager { if (escrowKey == null) { Slog.e(TAG, "Escrow key is null, but escrow was marked as ready"); - return false; + clearRebootEscrowIfNeeded(); + return ARM_REBOOT_ERROR_NO_ESCROW_KEY; } // We will use the same key from keystore to encrypt the escrow key and escrow data blob. SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey(); if (kk == null) { Slog.e(TAG, "Failed to get encryption key from keystore."); - return false; + clearRebootEscrowIfNeeded(); + return ARM_REBOOT_ERROR_KEYSTORE_FAILURE; } + + // TODO(b/183140900) design detailed errors for store escrow key errors. + // We don't clear rebootEscrow here, because some errors may be recoverable, e.g. network + // unavailable for server based provider. boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk); - if (armedRebootEscrow) { - mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM); - mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(), - USER_SYSTEM); - // Store the vbmeta digest of both slots. - mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false), - USER_SYSTEM); - mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, - mInjector.getVbmetaDigest(true), USER_SYSTEM); - mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM); - mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); - } - - return armedRebootEscrow; + if (!armedRebootEscrow) { + return ARM_REBOOT_ERROR_STORE_ESCROW_KEY; + } + + mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM); + mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(), + USER_SYSTEM); + // Store the vbmeta digest of both slots. + mStorage.setString(REBOOT_ESCROW_KEY_VBMETA_DIGEST, mInjector.getVbmetaDigest(false), + USER_SYSTEM); + mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, + mInjector.getVbmetaDigest(true), USER_SYSTEM); + mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM); + mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); + + return ARM_REBOOT_ERROR_NONE; } private void setRebootEscrowReady(boolean ready) { @@ -663,10 +688,6 @@ class RebootEscrowManager { } boolean clearRebootEscrow() { - if (mInjector.getRebootEscrowProvider() == null) { - return false; - } - clearRebootEscrowIfNeeded(); return true; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b10d56b62acc..abcf4fb939e1 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -384,7 +384,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (mPlaybackState == null) { return false; } - return mPlaybackState.isActiveState() == expected; + return mPlaybackState.isActive() == expected; } /** diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 91a66acf433d..dd80e167f0b3 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -636,9 +636,25 @@ public class LauncherAppsService extends SystemService { Objects.requireNonNull(component); // All right, create the sender. - Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component); + final int callingUid = injectBinderCallingUid(); final long identity = Binder.clearCallingIdentity(); try { + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + Intent packageIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT) + .setPackage(component.getPackageName()); + List<ResolveInfo> apps = pmInt.queryIntentActivities(packageIntent, + packageIntent.resolveTypeIfNeeded(mContext.getContentResolver()), + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, + callingUid, user.getIdentifier()); + // ensure that the component is present in the list + if (!apps.stream().anyMatch( + ri -> component.getClassName().equals(ri.activityInfo.name))) { + return null; + } + + Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component); final PendingIntent pi = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_CANCEL_CURRENT, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 284998203ac0..85c5a5ea84b8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -15315,9 +15315,8 @@ public class PackageManagerService extends IPackageManager.Stub final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions( REASON_LOCKED_BOOT_COMPLETED); am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null, - requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), - false, false, - userId); + requiredPermissions, null, android.app.AppOpsManager.OP_NONE, + bOptions.toBundle(), false, false, userId); // Deliver BOOT_COMPLETED only if user is unlocked final UserManagerInternal umInternal = mInjector.getUserManagerInternal(); @@ -15327,9 +15326,8 @@ public class PackageManagerService extends IPackageManager.Stub bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); } am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null, - requiredPermissions, android.app.AppOpsManager.OP_NONE, bOptions.toBundle(), - false, false, - userId); + requiredPermissions, null, android.app.AppOpsManager.OP_NONE, + bOptions.toBundle(), false, false, userId); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -22197,7 +22195,7 @@ public class PackageManagerService extends IPackageManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); try { am.broadcastIntentWithFeature(null, null, intent, null, null, - 0, null, null, null, android.app.AppOpsManager.OP_NONE, + 0, null, null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId); } catch (RemoteException e) { } @@ -27810,8 +27808,8 @@ public class PackageManagerService extends IPackageManager.Stub }; try { am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null, - requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false, - UserHandle.USER_ALL); + requiredPermissions, null, android.app.AppOpsManager.OP_NONE, null, false, + false, UserHandle.USER_ALL); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java index 2a17c6d4cec5..3f00a9d999aa 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java @@ -29,6 +29,7 @@ import android.content.pm.verify.domain.IDomainVerificationManager; import android.os.ServiceSpecificException; import java.util.List; +import java.util.Objects; import java.util.UUID; public class DomainVerificationManagerStub extends IDomainVerificationManager.Stub { @@ -110,6 +111,7 @@ public class DomainVerificationManagerStub extends IDomainVerificationManager.St public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) { try { + Objects.requireNonNull(domain); return mService.getOwnersForDomain(domain, userId); } catch (Exception e) { throw rethrow(e); diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java index 4ae79a209524..f0fdad016d55 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java @@ -742,6 +742,7 @@ public class DomainVerificationService extends SystemService } public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) { + Objects.requireNonNull(domain); mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(), userId); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 5b333e15fe67..29496b31b4a5 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4492,8 +4492,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean okToAnimate() { - return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep; + public boolean okToAnimate(boolean ignoreScreenOn) { + return (ignoreScreenOn || mDefaultDisplayPolicy.isAwake()) && !mDeviceGoingToSleep; } /** {@inheritDoc} */ diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d512edfb066a..78b03b2b88e7 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -897,12 +897,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public boolean isScreenOn(); /** + * @param ignoreScreenOn {@code true} if screen state should be ignored. * @return whether the device is currently allowed to animate. * * Note: this can be true even if it is not appropriate to animate for reasons that are outside * of the policy's authority. */ - boolean okToAnimate(); + boolean okToAnimate(boolean ignoreScreenOn); /** * Tell the policy that the lid switch has changed state. diff --git a/services/core/java/com/android/server/power/FaceDownDetector.java b/services/core/java/com/android/server/power/FaceDownDetector.java index 474ce59212ff..816c81ddf305 100644 --- a/services/core/java/com/android/server/power/FaceDownDetector.java +++ b/services/core/java/com/android/server/power/FaceDownDetector.java @@ -330,7 +330,9 @@ public class FaceDownDetector implements SensorEventListener { private boolean isEnabled() { return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_FEATURE_ENABLED, - DEFAULT_FEATURE_ENABLED); + DEFAULT_FEATURE_ENABLED) + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_flipToScreenOffEnabled); } private float getAccelerationThreshold() { diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 6ea030f56d4d..81a51e290664 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -25,6 +25,8 @@ import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIE import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE; + import android.annotation.IntDef; import android.content.Context; import android.content.IntentSender; @@ -47,6 +49,7 @@ import android.os.SystemProperties; import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.FastImmutableArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -143,7 +146,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo */ @IntDef({ ROR_NEED_PREPARATION, ROR_SKIP_PREPARATION_AND_NOTIFY, - ROR_SKIP_PREPARATION_NOT_NOTIFY}) + ROR_SKIP_PREPARATION_NOT_NOTIFY }) private @interface ResumeOnRebootActionsOnRequest {} /** @@ -151,10 +154,43 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo */ @IntDef({ ROR_NOT_REQUESTED, ROR_REQUESTED_NEED_CLEAR, - ROR_REQUESTED_SKIP_CLEAR}) + ROR_REQUESTED_SKIP_CLEAR }) private @interface ResumeOnRebootActionsOnClear {} /** + * Fatal arm escrow errors from lock settings that means the RoR is in a bad state. So clients + * need to prepare RoR again. + */ + static final FastImmutableArraySet<Integer> FATAL_ARM_ESCROW_ERRORS = + new FastImmutableArraySet<>(new Integer[]{ + LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY, + LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER, + LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH, + LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY, + LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE, + }); + + /** + * The error details for ArmRebootEscrow. It contains error codes from RecoverySystemService + * and LockSettingsService. + */ + static class RebootPreparationError { + final @ResumeOnRebootRebootErrorCode int mRebootErrorCode; + final int mProviderErrorCode; // The supplemental error code from lock settings + + RebootPreparationError(int rebootErrorCode, int providerErrorCode) { + mRebootErrorCode = rebootErrorCode; + mProviderErrorCode = providerErrorCode; + } + + int getErrorCodeForMetrics() { + // The ResumeOnRebootRebootErrorCode are aligned with 1000; so it's safe to add them + // for metrics purpose. + return mRebootErrorCode + mProviderErrorCode; + } + } + + /** * Manages shared preference, i.e. the storage used for metrics reporting. */ public static class PreferencesManager { @@ -709,34 +745,40 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return true; } - private @ResumeOnRebootRebootErrorCode int armRebootEscrow(String packageName, + private RebootPreparationError armRebootEscrow(String packageName, boolean slotSwitch) { if (packageName == null) { Slog.w(TAG, "Missing packageName when rebooting with lskf."); - return RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME; + return new RebootPreparationError( + RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME, ARM_REBOOT_ERROR_NONE); } if (!isLskfCaptured(packageName)) { - return RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED; + return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED, + ARM_REBOOT_ERROR_NONE); } if (!verifySlotForNextBoot(slotSwitch)) { - return RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH; + return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH, + ARM_REBOOT_ERROR_NONE); } final long origId = Binder.clearCallingIdentity(); - boolean result; + int providerErrorCode; try { - result = mInjector.getLockSettingsService().armRebootEscrow(); + providerErrorCode = mInjector.getLockSettingsService().armRebootEscrow(); } finally { Binder.restoreCallingIdentity(origId); } - if (!result) { - Slog.w(TAG, "Failure to escrow key for reboot"); - return RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE; + if (providerErrorCode != ARM_REBOOT_ERROR_NONE) { + Slog.w(TAG, "Failure to escrow key for reboot, providerErrorCode: " + + providerErrorCode); + return new RebootPreparationError( + RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE, providerErrorCode); } - return RESUME_ON_REBOOT_REBOOT_ERROR_NONE; + return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_NONE, + ARM_REBOOT_ERROR_NONE); } private boolean useServerBasedRoR() { @@ -750,7 +792,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo } private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch, - @ResumeOnRebootRebootErrorCode int errorCode) { + RebootPreparationError escrowError) { int uid = mInjector.getUidFromPackageName(packageName); boolean serverBased = useServerBasedRoR(); int preparedClientCount; @@ -773,15 +815,31 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo + " request count %d, lskf captured count %d, duration since lskf captured" + " %d seconds.", packageName, preparedClientCount, requestCount, lskfCapturedCount, durationSeconds)); - mInjector.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount, - requestCount, slotSwitch, serverBased, durationSeconds, lskfCapturedCount); + mInjector.reportRebootEscrowRebootMetrics(escrowError.getErrorCodeForMetrics(), uid, + preparedClientCount, requestCount, slotSwitch, serverBased, durationSeconds, + lskfCapturedCount); + } + + private void clearRoRPreparationStateOnRebootFailure(RebootPreparationError escrowError) { + if (!FATAL_ARM_ESCROW_ERRORS.contains(escrowError.mProviderErrorCode)) { + return; + } + + Slog.w(TAG, "Clearing resume on reboot states for all clients on arm escrow error: " + + escrowError.mProviderErrorCode); + synchronized (this) { + mCallerPendingRequest.clear(); + mCallerPreparedForReboot.clear(); + } } private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) { - @ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch); - reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode); + RebootPreparationError escrowError = armRebootEscrow(packageName, slotSwitch); + reportMetricsOnRebootWithLskf(packageName, slotSwitch, escrowError); + clearRoRPreparationStateOnRebootFailure(escrowError); + @ResumeOnRebootRebootErrorCode int errorCode = escrowError.mRebootErrorCode; if (errorCode != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) { return errorCode; } diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java index 4f5e8fa9c944..072cc16f680b 100644 --- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java @@ -16,26 +16,22 @@ package com.android.server.timedetector; -import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; -import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; -import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; - import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; -import android.os.Build; +import android.database.ContentObserver; +import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.Slog; -import com.android.internal.R; -import com.android.server.timedetector.TimeDetectorStrategy.Origin; +import com.android.internal.annotations.GuardedBy; +import com.android.server.timezonedetector.ConfigurationChangeListener; import java.time.Instant; import java.util.Objects; @@ -43,62 +39,71 @@ import java.util.Objects; /** * The real implementation of {@link TimeDetectorStrategyImpl.Environment} used on device. */ -public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { - - private static final String TAG = TimeDetectorService.TAG; - - private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; - - /** - * Time in the past. If automatic time suggestion is before this point, it's - * incorrect for sure. - */ - private static final Instant TIME_LOWER_BOUND = Instant.ofEpochMilli( - Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME)); - - /** - * By default telephony and network only suggestions are accepted and telephony takes - * precedence over network. - */ - private static final @Origin int[] DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = - { ORIGIN_TELEPHONY, ORIGIN_NETWORK }; +final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environment { - /** - * If a newly calculated system clock time and the current system clock time differs by this or - * more the system clock will actually be updated. Used to prevent the system clock being set - * for only minor differences. - */ - private final int mSystemClockUpdateThresholdMillis; + private static final String LOG_TAG = TimeDetectorService.TAG; @NonNull private final Context mContext; + @NonNull private final Handler mHandler; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final ContentResolver mContentResolver; @NonNull private final PowerManager.WakeLock mWakeLock; @NonNull private final AlarmManager mAlarmManager; @NonNull private final UserManager mUserManager; - @NonNull private final int[] mOriginPriorities; - public EnvironmentImpl(@NonNull Context context) { + // @NonNull after setConfigChangeListener() is called. + @GuardedBy("this") + private ConfigurationChangeListener mConfigChangeListener; + + EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, + @NonNull ServiceConfigAccessor serviceConfigAccessor) { mContext = Objects.requireNonNull(context); mContentResolver = Objects.requireNonNull(context.getContentResolver()); + mHandler = Objects.requireNonNull(handler); + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); PowerManager powerManager = context.getSystemService(PowerManager.class); mWakeLock = Objects.requireNonNull( - powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG)); + powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG)); mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class)); - mSystemClockUpdateThresholdMillis = - SystemProperties.getInt("ro.sys.time_detector_update_diff", - SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); + // Wire up the config change listeners. All invocations are performed on the mHandler + // thread. + + ContentResolver contentResolver = context.getContentResolver(); + contentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + handleAutoTimeDetectionChangedOnHandlerThread(); + } + }); + } + + /** Internal method for handling the auto time setting being changed. */ + private void handleAutoTimeDetectionChangedOnHandlerThread() { + synchronized (this) { + if (mConfigChangeListener == null) { + Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null"); + } + mConfigChangeListener.onChange(); + } + } - mOriginPriorities = getOriginPriorities(context); + @Override + public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) { + synchronized (this) { + mConfigChangeListener = Objects.requireNonNull(listener); + } } @Override public int systemClockUpdateThresholdMillis() { - return mSystemClockUpdateThresholdMillis; + return mServiceConfigAccessor.systemClockUpdateThresholdMillis(); } @Override @@ -112,12 +117,12 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @Override public Instant autoTimeLowerBound() { - return TIME_LOWER_BOUND; + return mServiceConfigAccessor.autoTimeLowerBound(); } @Override public int[] autoOriginPriorities() { - return mOriginPriorities; + return mServiceConfigAccessor.getOriginPriorities(); } @Override @@ -131,7 +136,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @Override public void acquireWakeLock() { if (mWakeLock.isHeld()) { - Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held"); + Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " already held"); } mWakeLock.acquire(); } @@ -160,7 +165,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme private void checkWakeLockHeld() { if (!mWakeLock.isHeld()) { - Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held"); + Slog.wtf(LOG_TAG, "WakeLock " + mWakeLock + " not held"); } } @@ -168,20 +173,4 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme UserHandle userHandle = UserHandle.of(userId); return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); } - - private static int[] getOriginPriorities(@NonNull Context context) { - String[] originStrings = - context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); - if (originStrings.length == 0) { - return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; - } else { - int[] origins = new int[originStrings.length]; - for (int i = 0; i < originStrings.length; i++) { - int origin = stringToOrigin(originStrings[i]); - origins[i] = origin; - } - - return origins; - } - } } diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java new file mode 100644 index 000000000000..be4432ab61bf --- /dev/null +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java @@ -0,0 +1,141 @@ +/* + * Copyright 2021 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.timedetector; + +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; +import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Build; +import android.os.SystemProperties; +import android.util.ArraySet; + +import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.server.timezonedetector.ConfigurationChangeListener; + +import java.time.Instant; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * A singleton that provides access to service configuration for time detection. This hides how + * configuration is split between static, compile-time config and dynamic, server-pushed flags. It + * provides a rudimentary mechanism to signal when values have changed. + */ +final class ServiceConfigAccessor { + + private static final int SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT = 2 * 1000; + + /** + * By default telephony and network only suggestions are accepted and telephony takes + * precedence over network. + */ + private static final @TimeDetectorStrategy.Origin int[] + DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = { ORIGIN_TELEPHONY, ORIGIN_NETWORK }; + + /** + * Time in the past. If an automatic time suggestion is before this point, it is sure to be + * incorrect. + */ + private static final Instant TIME_LOWER_BOUND_DEFAULT = Instant.ofEpochMilli( + Long.max(android.os.Environment.getRootDirectory().lastModified(), Build.TIME)); + + private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet( + new ArraySet<>(new String[] { + })); + + private static final Object SLOCK = new Object(); + + /** The singleton instance. Initialized once in {@link #getInstance(Context)}. */ + @GuardedBy("SLOCK") + @Nullable + private static ServiceConfigAccessor sInstance; + + @NonNull private final Context mContext; + @NonNull private final ServerFlags mServerFlags; + @NonNull private final int[] mOriginPriorities; + + /** + * If a newly calculated system clock time and the current system clock time differs by this or + * more the system clock will actually be updated. Used to prevent the system clock being set + * for only minor differences. + */ + private final int mSystemClockUpdateThresholdMillis; + + private ServiceConfigAccessor(@NonNull Context context) { + mContext = Objects.requireNonNull(context); + mServerFlags = ServerFlags.getInstance(mContext); + mOriginPriorities = getOriginPrioritiesInternal(); + mSystemClockUpdateThresholdMillis = + SystemProperties.getInt("ro.sys.time_detector_update_diff", + SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); + } + + /** Returns the singleton instance. */ + static ServiceConfigAccessor getInstance(Context context) { + synchronized (SLOCK) { + if (sInstance == null) { + sInstance = new ServiceConfigAccessor(context); + } + return sInstance; + } + } + + /** + * Adds a listener that will be called when server flags related to this class change. The + * callbacks are delivered on the main looper thread. + * + * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove + * method. + */ + void addListener(@NonNull ConfigurationChangeListener listener) { + mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH); + } + + @NonNull + int[] getOriginPriorities() { + return mOriginPriorities; + } + + int systemClockUpdateThresholdMillis() { + return mSystemClockUpdateThresholdMillis; + } + + Instant autoTimeLowerBound() { + return TIME_LOWER_BOUND_DEFAULT; + } + + private int[] getOriginPrioritiesInternal() { + String[] originStrings = + mContext.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); + if (originStrings.length == 0) { + return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; + } else { + int[] origins = new int[originStrings.length]; + for (int i = 0; i < originStrings.length; i++) { + int origin = stringToOrigin(originStrings[i]); + origins[i] = origin; + } + + return origins; + } + } +} diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 27e2ee585a20..20c1c3c8b738 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -27,12 +27,9 @@ import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; -import android.content.ContentResolver; import android.content.Context; -import android.database.ContentObserver; import android.os.Binder; import android.os.Handler; -import android.provider.Settings; import android.util.IndentingPrintWriter; import com.android.internal.annotations.VisibleForTesting; @@ -64,7 +61,16 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Override public void onStart() { - TimeDetectorService service = TimeDetectorService.create(getContext()); + Context context = getContext(); + Handler handler = FgThread.getHandler(); + + ServiceConfigAccessor serviceConfigAccessor = + ServiceConfigAccessor.getInstance(context); + TimeDetectorStrategy timeDetectorStrategy = + TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); + + TimeDetectorService service = + new TimeDetectorService(context, handler, timeDetectorStrategy); // Publish the binder service so it can be accessed from other (appropriately // permissioned) processes. @@ -77,28 +83,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; @NonNull private final CallerIdentityInjector mCallerIdentityInjector; - private static TimeDetectorService create(@NonNull Context context) { - TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context); - TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl(environment); - - Handler handler = FgThread.getHandler(); - TimeDetectorService timeDetectorService = - new TimeDetectorService(context, handler, timeDetectorStrategy); - - // Wire up event listening. - ContentResolver contentResolver = context.getContentResolver(); - contentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true, - new ContentObserver(handler) { - @Override - public void onChange(boolean selfChange) { - timeDetectorService.handleAutoTimeDetectionChanged(); - } - }); - - return timeDetectorService; - } - @VisibleForTesting public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, @NonNull TimeDetectorStrategy timeDetectorStrategy) { @@ -185,12 +169,6 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal)); } - /** Internal method for handling the auto time setting being changed. */ - @VisibleForTesting - public void handleAutoTimeDetectionChanged() { - mHandler.post(mTimeDetectorStrategy::handleAutoTimeConfigChanged); - } - @Override protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args) { diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index cde66becdee2..be382f0409b1 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -92,12 +92,6 @@ public interface TimeDetectorStrategy extends Dumpable { /** Returns the configuration that controls time detector behaviour for specified user. */ ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); - /** - * Handles the auto-time configuration changing For example, when the auto-time setting is - * toggled on or off. - */ - void handleAutoTimeConfigChanged(); - // Utility methods below are to be moved to a better home when one becomes more obvious. /** diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 289d8d6e648e..db8a59e0ab26 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -29,6 +29,8 @@ import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; +import android.content.Context; +import android.os.Handler; import android.os.TimestampedValue; import android.util.IndentingPrintWriter; import android.util.LocalLog; @@ -37,6 +39,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.timezonedetector.ArrayMapWithHistory; +import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ReferenceWithHistory; import java.time.Instant; @@ -132,6 +135,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { public interface Environment { /** + * Sets a {@link ConfigurationChangeListener} that will be invoked when there are any + * changes that could affect time detection. This is invoked during system server setup. + */ + void setConfigChangeListener(@NonNull ConfigurationChangeListener listener); + + /** * The absolute threshold below which the system clock need not be updated. i.e. if setting * the system clock would adjust it by less than this (either backwards or forwards) then it * need not be set. @@ -178,8 +187,19 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { void releaseWakeLock(); } + static TimeDetectorStrategy create( + @NonNull Context context, @NonNull Handler handler, + @NonNull ServiceConfigAccessor serviceConfigAccessor) { + + TimeDetectorStrategyImpl.Environment environment = + new EnvironmentImpl(context, handler, serviceConfigAccessor); + return new TimeDetectorStrategyImpl(environment); + } + + @VisibleForTesting TimeDetectorStrategyImpl(@NonNull Environment environment) { mEnvironment = Objects.requireNonNull(environment); + mEnvironment.setConfigChangeListener(this::handleAutoTimeConfigChanged); } @Override @@ -279,8 +299,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return mEnvironment.configurationInternal(userId); } - @Override - public synchronized void handleAutoTimeConfigChanged() { + private synchronized void handleAutoTimeConfigChanged() { boolean enabled = mEnvironment.isAutoTimeDetectionEnabled(); // When automatic time detection is enabled we update the system clock instantly if we can. // Conversely, when automatic time detection is disabled we leave the clock as it is. diff --git a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java index 0e5f3bfbb4b1..b84f8a850ba7 100644 --- a/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timezonedetector/EnvironmentImpl.java @@ -47,7 +47,7 @@ import java.util.Optional; /** * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}. */ -public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment { +final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Environment { private static final String LOG_TAG = TimeZoneDetectorService.TAG; private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java index 50d37f42e889..dddb11b7e249 100644 --- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java +++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java @@ -133,8 +133,8 @@ public final class ServiceConfigAccessor { } /** - * Adds a listener that will be called server flags related to this class change. The callbacks - * are delivered on the main looper thread. + * Adds a listener that will be called when server flags related to this class change. The + * callbacks are delivered on the main looper thread. * * <p>Note: Only for use by long-lived objects. There is deliberately no associated remove * method. diff --git a/services/core/java/com/android/server/trust/OWNERS b/services/core/java/com/android/server/trust/OWNERS index b039c4b45447..e2c6ce15b51e 100644 --- a/services/core/java/com/android/server/trust/OWNERS +++ b/services/core/java/com/android/server/trust/OWNERS @@ -1 +1 @@ -include /core/java/android/app/trust/OWNERS +include /core/java/android/service/trust/OWNERS diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index d8a145d9ae33..19fbdbd86099 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -43,6 +43,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.IndentingPrintWriter; import java.util.Collections; import java.util.HashMap; @@ -320,6 +321,17 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { && mPrivilegedPackages.equals(other.mPrivilegedPackages); } + /** Dumps the state of this snapshot for logging and debugging purposes. */ + public void dump(IndentingPrintWriter pw) { + pw.println("TelephonySubscriptionSnapshot:"); + pw.increaseIndent(); + + pw.println("mSubIdToGroupMap: " + mSubIdToGroupMap); + pw.println("mPrivilegedPackages: " + mPrivilegedPackages); + + pw.decreaseIndent(); + } + @Override public String toString() { return "TelephonySubscriptionSnapshot{ " diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java index ab9de77005b3..a59b368ec321 100644 --- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java +++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java @@ -31,6 +31,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import java.util.ArrayList; @@ -396,6 +397,18 @@ public class UnderlyingNetworkTracker { return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); } + /** Dumps the state of this record for logging and debugging purposes. */ + public void dump(IndentingPrintWriter pw) { + pw.println("UnderlyingNetworkRecord:"); + pw.increaseIndent(); + + pw.println("mNetwork: " + network); + pw.println("mNetworkCapabilities: " + networkCapabilities); + pw.println("mLinkProperties: " + linkProperties); + + pw.decreaseIndent(); + } + /** Builder to incrementally construct an UnderlyingNetworkRecord. */ private static class Builder { @NonNull private final Network mNetwork; diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 7bc6056f91f3..cccb0968fc6a 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -16,6 +16,8 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; @@ -26,14 +28,21 @@ import static com.android.server.VcnManagementService.VDBG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.ContentResolver; +import android.database.ContentObserver; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.NetworkScore; +import android.net.Uri; import android.net.vcn.VcnConfig; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager.VcnErrorCode; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -42,9 +51,11 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -61,6 +72,11 @@ import java.util.Set; public class Vcn extends Handler { private static final String TAG = Vcn.class.getSimpleName(); + private static final int VCN_LEGACY_SCORE_INT = 52; + + private static final List<Integer> CAPS_REQUIRING_MOBILE_DATA = + Arrays.asList(NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN); + private static final int MSG_EVENT_BASE = 0; private static final int MSG_CMD_BASE = 100; @@ -110,6 +126,15 @@ public class Vcn extends Handler { */ private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4; + /** + * Triggers reevaluation of mobile data enabled conditions. + * + * <p>Upon this notification, the VCN will check if any of the underlying subIds have mobile + * data enabled. If not, the VCN will restart any GatewayConnections providing INTERNET or DUN + * with the current mobile data toggle status. + */ + private static final int MSG_EVENT_MOBILE_DATA_TOGGLED = MSG_EVENT_BASE + 5; + /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; @@ -118,6 +143,8 @@ public class Vcn extends Handler { @NonNull private final Dependencies mDeps; @NonNull private final VcnNetworkRequestListener mRequestListener; @NonNull private final VcnCallback mVcnCallback; + @NonNull private final VcnContentResolver mContentResolver; + @NonNull private final ContentObserver mMobileDataSettingsObserver; /** * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs. @@ -154,6 +181,8 @@ public class Vcn extends Handler { // Accessed from different threads, but always under lock in VcnManagementService private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE; + private boolean mIsMobileDataEnabled = false; + public Vcn( @NonNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @@ -177,10 +206,19 @@ public class Vcn extends Handler { mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback"); mDeps = Objects.requireNonNull(deps, "Missing deps"); mRequestListener = new VcnNetworkRequestListener(); + mContentResolver = mDeps.newVcnContentResolver(mVcnContext); + mMobileDataSettingsObserver = new VcnMobileDataContentObserver(this /* handler */); + + final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); + mContentResolver.registerContentObserver( + uri, true /* notifyForDescendants */, mMobileDataSettingsObserver); mConfig = Objects.requireNonNull(config, "Missing config"); mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); + // Update mIsMobileDataEnabled before starting handling of NetworkRequests. + mIsMobileDataEnabled = getMobileDataStatus(); + // Register to receive cached and future NetworkRequests mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); } @@ -230,10 +268,10 @@ public class Vcn extends Handler { private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener { @Override - public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { + public void onNetworkRequested(@NonNull NetworkRequest request) { Objects.requireNonNull(request, "Missing request"); - sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, score, providerId, request)); + sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, request)); } } @@ -249,7 +287,7 @@ public class Vcn extends Handler { handleConfigUpdated((VcnConfig) msg.obj); break; case MSG_EVENT_NETWORK_REQUESTED: - handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2); + handleNetworkRequested((NetworkRequest) msg.obj); break; case MSG_EVENT_SUBSCRIPTIONS_CHANGED: handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj); @@ -260,6 +298,9 @@ public class Vcn extends Handler { case MSG_EVENT_SAFE_MODE_STATE_CHANGED: handleSafeModeStatusChanged(); break; + case MSG_EVENT_MOBILE_DATA_TOGGLED: + handleMobileDataToggled(); + break; case MSG_CMD_TEARDOWN: handleTeardown(); break; @@ -327,25 +368,9 @@ public class Vcn extends Handler { } } - private void handleNetworkRequested( - @NonNull NetworkRequest request, int score, int providerId) { + private void handleNetworkRequested(@NonNull NetworkRequest request) { Slog.v(getLogTag(), "Received request " + request); - if (score > getNetworkScore()) { - if (VDBG) { - Slog.v( - getLogTag(), - "Request already satisfied by higher-scoring (" - + score - + ") network from " - + "provider " - + providerId - + ": " - + request); - } - return; - } - // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { @@ -366,18 +391,37 @@ public class Vcn extends Handler { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request); + if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) { + // Skip; this network does not provide any services if mobile data is disabled. + continue; + } + final VcnGatewayConnection vcnGatewayConnection = mDeps.newVcnGatewayConnection( mVcnContext, mSubscriptionGroup, mLastSnapshot, gatewayConnectionConfig, - new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig)); + new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig), + mIsMobileDataEnabled); mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); } } } + private Set<Integer> getExposedCapabilitiesForMobileDataState( + VcnGatewayConnectionConfig gatewayConnectionConfig) { + if (mIsMobileDataEnabled) { + return gatewayConnectionConfig.getAllExposedCapabilities(); + } + + final Set<Integer> exposedCapsWithoutMobileData = + new ArraySet<>(gatewayConnectionConfig.getAllExposedCapabilities()); + exposedCapsWithoutMobileData.removeAll(CAPS_REQUIRING_MOBILE_DATA); + + return exposedCapsWithoutMobileData; + } + private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config); mVcnGatewayConnections.remove(config); @@ -396,12 +440,55 @@ public class Vcn extends Handler { } } + private void handleMobileDataToggled() { + final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled; + mIsMobileDataEnabled = getMobileDataStatus(); + + if (oldMobileDataEnabledStatus != mIsMobileDataEnabled) { + // Teardown any GatewayConnections that advertise INTERNET or DUN. If they provide other + // services, the VcnGatewayConnections will be restarted without advertising INTERNET or + // DUN. + for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : + mVcnGatewayConnections.entrySet()) { + final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey(); + final VcnGatewayConnection gatewayConnection = entry.getValue(); + + final Set<Integer> exposedCaps = + gatewayConnectionConfig.getAllExposedCapabilities(); + if (exposedCaps.contains(NET_CAPABILITY_INTERNET) + || exposedCaps.contains(NET_CAPABILITY_DUN)) { + if (gatewayConnection == null) { + Slog.wtf( + getLogTag(), + "Found gatewayConnectionConfig without GatewayConnection"); + } else { + // TODO(b/184868850): Optimize by restarting NetworkAgents without teardown. + gatewayConnection.teardownAsynchronously(); + } + } + } + } + } + + private boolean getMobileDataStatus() { + final TelephonyManager genericTelMan = + mVcnContext.getContext().getSystemService(TelephonyManager.class); + + for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { + if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) { + return true; + } + } + + return false; + } + private boolean isRequestSatisfiedByGatewayConnectionConfig( @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); builder.addTransportType(TRANSPORT_CELLULAR); builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); - for (int cap : config.getAllExposedCapabilities()) { + for (int cap : getExposedCapabilitiesForMobileDataState(config)) { builder.addCapability(cap); } @@ -432,12 +519,20 @@ public class Vcn extends Handler { pw.decreaseIndent(); } + @VisibleForTesting(visibility = Visibility.PRIVATE) + public boolean isMobileDataEnabled() { + return mIsMobileDataEnabled; + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) + public void setMobileDataEnabled(boolean isMobileDataEnabled) { + mIsMobileDataEnabled = isMobileDataEnabled; + } + /** Retrieves the network score for a VCN Network */ - // Package visibility for use in VcnGatewayConnection - static int getNetworkScore() { - // TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in - // subGrp" value - return 52; + // Package visibility for use in VcnGatewayConnection and VcnNetworkProvider + static NetworkScore getNetworkScore() { + return new NetworkScore.Builder().setLegacyInt(VCN_LEGACY_SCORE_INT).build(); } /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ @@ -485,6 +580,17 @@ public class Vcn extends Handler { } } + private class VcnMobileDataContentObserver extends ContentObserver { + private VcnMobileDataContentObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED)); + } + } + /** External dependencies used by Vcn, for injection in tests */ @VisibleForTesting(visibility = Visibility.PRIVATE) public static class Dependencies { @@ -494,13 +600,36 @@ public class Vcn extends Handler { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, VcnGatewayConnectionConfig connectionConfig, - VcnGatewayStatusCallback gatewayStatusCallback) { + VcnGatewayStatusCallback gatewayStatusCallback, + boolean isMobileDataEnabled) { return new VcnGatewayConnection( vcnContext, subscriptionGroup, snapshot, connectionConfig, - gatewayStatusCallback); + gatewayStatusCallback, + isMobileDataEnabled); + } + + /** Builds a new VcnContentResolver instance */ + public VcnContentResolver newVcnContentResolver(VcnContext vcnContext) { + return new VcnContentResolver(vcnContext); + } + } + + /** Proxy Implementation of NetworkAgent, used for testing. */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class VcnContentResolver { + private final ContentResolver mImpl; + + public VcnContentResolver(VcnContext vcnContext) { + mImpl = vcnContext.getContext().getContentResolver(); + } + + /** Registers the content observer */ + public void registerContentObserver( + @NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer) { + mImpl.registerContentObserver(uri, notifyForDescendants, observer); } } } diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 1d55ba464f51..23fb95b4fa40 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -16,6 +16,8 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; @@ -47,6 +49,7 @@ import android.net.NetworkAgent; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; import android.net.NetworkProvider; +import android.net.NetworkScore; import android.net.RouteInfo; import android.net.TelephonyNetworkSpecifier; import android.net.Uri; @@ -517,6 +520,7 @@ public class VcnGatewayConnection extends StateMachine { @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback; @NonNull private final Dependencies mDeps; @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback; + private final boolean mIsMobileDataEnabled; @NonNull private final IpSecManager mIpSecManager; @@ -626,13 +630,15 @@ public class VcnGatewayConnection extends StateMachine { @NonNull ParcelUuid subscriptionGroup, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig, - @NonNull VcnGatewayStatusCallback gatewayStatusCallback) { + @NonNull VcnGatewayStatusCallback gatewayStatusCallback, + boolean isMobileDataEnabled) { this( vcnContext, subscriptionGroup, snapshot, connectionConfig, gatewayStatusCallback, + isMobileDataEnabled, new Dependencies()); } @@ -643,6 +649,7 @@ public class VcnGatewayConnection extends StateMachine { @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull VcnGatewayStatusCallback gatewayStatusCallback, + boolean isMobileDataEnabled, @NonNull Dependencies deps) { super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; @@ -650,6 +657,7 @@ public class VcnGatewayConnection extends StateMachine { mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); mGatewayStatusCallback = Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback"); + mIsMobileDataEnabled = isMobileDataEnabled; mDeps = Objects.requireNonNull(deps, "Missing deps"); mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); @@ -1502,7 +1510,7 @@ public class VcnGatewayConnection extends StateMachine { @NonNull VcnNetworkAgent agent, @NonNull VcnChildSessionConfiguration childConfig) { final NetworkCapabilities caps = - buildNetworkCapabilities(mConnectionConfig, mUnderlying); + buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled); final LinkProperties lp = buildConnectedLinkProperties( mConnectionConfig, tunnelIface, childConfig, mUnderlying); @@ -1515,7 +1523,7 @@ public class VcnGatewayConnection extends StateMachine { @NonNull IpSecTunnelInterface tunnelIface, @NonNull VcnChildSessionConfiguration childConfig) { final NetworkCapabilities caps = - buildNetworkCapabilities(mConnectionConfig, mUnderlying); + buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled); final LinkProperties lp = buildConnectedLinkProperties( mConnectionConfig, tunnelIface, childConfig, mUnderlying); @@ -1843,7 +1851,8 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) static NetworkCapabilities buildNetworkCapabilities( @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, - @Nullable UnderlyingNetworkRecord underlying) { + @Nullable UnderlyingNetworkRecord underlying, + boolean isMobileDataEnabled) { final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); builder.addTransportType(TRANSPORT_CELLULAR); @@ -1853,6 +1862,12 @@ public class VcnGatewayConnection extends StateMachine { // Add exposed capabilities for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) { + // Skip adding INTERNET or DUN if mobile data is disabled. + if (!isMobileDataEnabled + && (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) { + continue; + } + builder.addCapability(cap); } @@ -2040,6 +2055,12 @@ public class VcnGatewayConnection extends StateMachine { "mNetworkAgent.getNetwork(): " + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork())); + pw.println("mUnderlying:"); + pw.increaseIndent(); + mUnderlying.dump(pw); + pw.decreaseIndent(); + pw.println(); + pw.decreaseIndent(); } @@ -2183,7 +2204,7 @@ public class VcnGatewayConnection extends StateMachine { @NonNull String tag, @NonNull NetworkCapabilities caps, @NonNull LinkProperties lp, - @NonNull int score, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig nac, @NonNull NetworkProvider provider, @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, @@ -2324,7 +2345,7 @@ public class VcnGatewayConnection extends StateMachine { @NonNull String tag, @NonNull NetworkCapabilities caps, @NonNull LinkProperties lp, - @NonNull int score, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig nac, @NonNull NetworkProvider provider, @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java index be0deb57ee76..72cd7880325f 100644 --- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java +++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java @@ -16,14 +16,25 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; + import static com.android.server.VcnManagementService.VDBG; import android.annotation.NonNull; import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; import android.net.NetworkProvider; import android.net.NetworkRequest; +import android.net.NetworkScore; +import android.net.vcn.VcnGatewayConnectionConfig; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -33,6 +44,7 @@ import com.android.internal.util.IndentingPrintWriter; import java.util.Objects; import java.util.Set; +import java.util.concurrent.Executor; /** * VCN Network Provider routes NetworkRequests to listeners to bring up tunnels as needed. @@ -47,15 +59,70 @@ public class VcnNetworkProvider extends NetworkProvider { private final Set<NetworkRequestListener> mListeners = new ArraySet<>(); + private final Context mContext; + private final Handler mHandler; + private final Dependencies mDeps; + /** - * Cache of NetworkRequest(s), scores and network providers, keyed by NetworkRequest + * Cache of NetworkRequest(s). * * <p>NetworkRequests are immutable once created, and therefore can be used as stable keys. */ - private final ArrayMap<NetworkRequest, NetworkRequestEntry> mRequests = new ArrayMap<>(); + private final Set<NetworkRequest> mRequests = new ArraySet<>(); + + public VcnNetworkProvider(@NonNull Context context, @NonNull Looper looper) { + this(context, looper, new Dependencies()); + } - public VcnNetworkProvider(Context context, Looper looper) { - super(context, looper, VcnNetworkProvider.class.getSimpleName()); + @VisibleForTesting(visibility = Visibility.PRIVATE) + public VcnNetworkProvider( + @NonNull Context context, @NonNull Looper looper, @NonNull Dependencies dependencies) { + super( + Objects.requireNonNull(context, "Missing context"), + Objects.requireNonNull(looper, "Missing looper"), + TAG); + + mContext = context; + mHandler = new Handler(looper); + mDeps = Objects.requireNonNull(dependencies, "Missing dependencies"); + } + + /** Registers this VcnNetworkProvider and a generic network offer with ConnectivityService. */ + public void register() { + mContext.getSystemService(ConnectivityManager.class).registerNetworkProvider(this); + mDeps.registerNetworkOffer( + this, + Vcn.getNetworkScore(), // score filter + buildCapabilityFilter(), + new HandlerExecutor(mHandler), + new NetworkOfferCallback() { + @Override + public void onNetworkNeeded(@NonNull NetworkRequest request) { + handleNetworkRequested(request); + } + + @Override + public void onNetworkUnneeded(@NonNull NetworkRequest request) { + handleNetworkRequestWithdrawn(request); + } + }); + } + + /** Builds the filter for NetworkRequests that can be served by the VcnNetworkProvider. */ + private NetworkCapabilities buildCapabilityFilter() { + final NetworkCapabilities.Builder builder = + new NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_TRUSTED) + .addCapability(NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NET_CAPABILITY_NOT_VPN) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + + for (int cap : VcnGatewayConnectionConfig.ALLOWED_CAPABILITIES) { + builder.addCapability(cap); + } + + return builder.build(); } /** @@ -80,77 +147,37 @@ public class VcnNetworkProvider extends NetworkProvider { /** Sends all cached NetworkRequest(s) to the specified listener. */ @VisibleForTesting(visibility = Visibility.PACKAGE) public void resendAllRequests(@NonNull NetworkRequestListener listener) { - for (NetworkRequestEntry entry : mRequests.values()) { - notifyListenerForEvent(listener, entry); + for (NetworkRequest request : mRequests) { + notifyListenerForEvent(listener, request); } } private void notifyListenerForEvent( - @NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) { - listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId); + @NonNull NetworkRequestListener listener, @NonNull NetworkRequest request) { + listener.onNetworkRequested(request); } - @Override - public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { + private void handleNetworkRequested(@NonNull NetworkRequest request) { if (VDBG) { - Slog.v( - TAG, - "Network requested: Request = " - + request - + ", score = " - + score - + ", providerId = " - + providerId); + Slog.v(TAG, "Network requested: Request = " + request); } - final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId); - - // NetworkRequests are immutable once created, and therefore can be used as stable keys. - mRequests.put(request, entry); + mRequests.add(request); // TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on // Default Data Sub, or similar) for (NetworkRequestListener listener : mListeners) { - notifyListenerForEvent(listener, entry); + notifyListenerForEvent(listener, request); } } - @Override - public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) { + private void handleNetworkRequestWithdrawn(@NonNull NetworkRequest request) { mRequests.remove(request); } - private static class NetworkRequestEntry { - public final NetworkRequest mRequest; - public final int mScore; - public final int mProviderId; - - private NetworkRequestEntry(@NonNull NetworkRequest request, int score, int providerId) { - mRequest = Objects.requireNonNull(request, "Missing request"); - mScore = score; - mProviderId = providerId; - } - - /** - * Dumps the state of this NetworkRequestEntry for logging and debugging purposes. - * - * <p>PII and credentials MUST NEVER be dumped here. - */ - public void dump(IndentingPrintWriter pw) { - pw.println("NetworkRequestEntry:"); - pw.increaseIndent(); - - pw.println("mRequest: " + mRequest); - pw.println("mScore: " + mScore); - pw.println("mProviderId: " + mProviderId); - - pw.decreaseIndent(); - } - } - // package-private interface NetworkRequestListener { - void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId); + void onNetworkRequested(@NonNull NetworkRequest request); } /** @@ -163,17 +190,35 @@ public class VcnNetworkProvider extends NetworkProvider { pw.increaseIndent(); pw.println("mListeners:"); + pw.increaseIndent(); for (NetworkRequestListener listener : mListeners) { pw.println(listener); } + pw.decreaseIndent(); pw.println(); - pw.println("mRequests.values:"); - for (NetworkRequestEntry entry : mRequests.values()) { - entry.dump(pw); + pw.println("mRequests:"); + pw.increaseIndent(); + for (NetworkRequest request : mRequests) { + pw.println(request); } + pw.decreaseIndent(); pw.println(); pw.decreaseIndent(); } + + /** Proxy class for dependencies used for testing. */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static class Dependencies { + /** Registers a given network offer for the given provider. */ + public void registerNetworkOffer( + @NonNull VcnNetworkProvider provider, + @NonNull NetworkScore score, + @NonNull NetworkCapabilities capabilitiesFilter, + @NonNull Executor executor, + @NonNull NetworkOfferCallback callback) { + provider.registerNetworkOffer(score, capabilitiesFilter, executor, callback); + } + } } diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java index 49c1a02215e3..5d40cca3e19d 100644 --- a/services/core/java/com/android/server/vcn/util/MtuUtils.java +++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java @@ -113,7 +113,6 @@ public class MtuUtils { return IPV6_MIN_MTU; } - boolean hasUnknownAlgorithm = false; int maxAuthOverhead = 0; int maxCryptOverhead = 0; int maxAuthCryptOverhead = 0; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5b4e6a032050..f5cfc9d86cf4 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4655,7 +4655,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // still check DC#okToAnimate again if the transition animation is fine to apply. // TODO(new-app-transition): Rewrite this logic using WM Shell. final boolean recentsAnimating = isAnimating(PARENTS, ANIMATION_TYPE_RECENTS); - if (okToAnimate(true /* ignoreFrozen */) && (appTransition.isTransitionSet() + if (okToAnimate(true /* ignoreFrozen */, canTurnScreenOn()) + && (appTransition.isTransitionSet() || (recentsAnimating && !isActivityTypeHome()))) { if (visible) { displayContent.mOpeningApps.add(this); @@ -5862,6 +5863,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A nowVisible = true; lastVisibleTime = SystemClock.uptimeMillis(); mAtmService.scheduleAppGcsLocked(); + // The nowVisible may be false in onAnimationFinished because the transition animation + // was started by starting window but the main window hasn't drawn so the procedure + // didn't schedule. Hence also check when nowVisible becomes true (drawn) to avoid the + // closing activity having to wait until idle timeout to be stopped or destroyed if the + // next activity won't report idle (e.g. repeated view animation). + mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); } } @@ -6678,26 +6685,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // traverse the copy. final ArrayList<WindowState> children = new ArrayList<>(mChildren); children.forEach(WindowState::onExitAnimationDone); + // The starting window could transfer to another activity after app transition started, in + // that case the latest top activity might not receive exit animation done callback if the + // starting window didn't applied exit animation success. Notify animation finish to the + // starting window if needed. + if (task != null && startingMoved) { + final WindowState transferredStarting = task.getWindow(w -> + w.mAttrs.type == TYPE_APPLICATION_STARTING); + if (transferredStarting != null && transferredStarting.mAnimatingExit + && !transferredStarting.isSelfAnimating(0 /* flags */, + ANIMATION_TYPE_WINDOW_ANIMATION)) { + transferredStarting.onExitAnimationDone(); + } + } getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token); scheduleAnimation(); - if (!mTaskSupervisor.mStoppingActivities.isEmpty() - || !mTaskSupervisor.mFinishingActivities.isEmpty()) { - if (mRootWindowContainer.allResumedActivitiesIdle()) { - // If all activities are already idle then we now need to make sure we perform - // the full stop of this activity. This is because we won't do that while they - // are still waiting for the animation to finish. - mTaskSupervisor.scheduleIdle(); - } else if (mRootWindowContainer.allResumedActivitiesVisible()) { - // If all resumed activities are already visible (and should be drawn, see - // updateReportedVisibility ~ nowVisible) but not idle, we still schedule to - // process the stopping and finishing activities because the transition is done. - // This also avoids if the next activity never reports idle (e.g. animating view), - // the previous will need to wait until idle timeout to be stopped or destroyed. - mTaskSupervisor.scheduleProcessStoppingAndFinishingActivities(); - } - } + // Schedule to handle the stopping and finishing activities which the animation is done + // because the activities which were animating have not been stopped yet. + mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index bdde3692ef53..2b1cf395dcd7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2027,7 +2027,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } final void scheduleIdle() { - mHandler.sendEmptyMessage(IDLE_NOW_MSG); + if (!mHandler.hasMessages(IDLE_NOW_MSG)) { + mHandler.sendEmptyMessage(IDLE_NOW_MSG); + } } /** @@ -2115,8 +2117,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } - void scheduleProcessStoppingAndFinishingActivities() { - if (!mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)) { + void scheduleProcessStoppingAndFinishingActivitiesIfNeeded() { + if ((!mStoppingActivities.isEmpty() || !mFinishingActivities.isEmpty()) + && !mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG) + && mRootWindowContainer.allResumedActivitiesVisible()) { mHandler.sendEmptyMessage(PROCESS_STOPPING_AND_FINISHING_MSG); } } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 99289e0139b0..394ae7e94b72 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1679,11 +1679,15 @@ public class AppTransition implements Dump { || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; } - static boolean isKeyguardTransitOld(@TransitionOldType int transit) { - return isKeyguardGoingAwayTransitOld(transit) || transit == TRANSIT_OLD_KEYGUARD_OCCLUDE + static boolean isKeyguardOccludeTransitOld(@TransitionOldType int transit) { + return transit == TRANSIT_OLD_KEYGUARD_OCCLUDE || transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } + static boolean isKeyguardTransitOld(@TransitionOldType int transit) { + return isKeyguardGoingAwayTransitOld(transit) || isKeyguardOccludeTransitOld(transit); + } + static boolean isTaskTransitOld(@TransitionOldType int transit) { return isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_TASK_CLOSE diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 9a43ca8794da..e7c51a4ac65a 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -68,7 +68,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Trace; import android.util.ArrayMap; @@ -428,43 +427,37 @@ public class AppTransitionController { return mainWindow != null ? mainWindow.mAttrs : null; } - RemoteAnimationAdapter getRemoteAnimationOverride(@NonNull WindowContainer container, + RemoteAnimationAdapter getRemoteAnimationOverride(@Nullable WindowContainer container, @TransitionOldType int transit, ArraySet<Integer> activityTypes) { - final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition(); - if (definition != null) { - final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes); - if (adapter != null) { - return adapter; - } - } - if (mRemoteAnimationDefinition != null) { - final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter( - transit, activityTypes); - if (adapter != null) { - return adapter; + if (container != null) { + final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition(); + if (definition != null) { + final RemoteAnimationAdapter adapter = definition.getAdapter(transit, + activityTypes); + if (adapter != null) { + return adapter; + } } } - return null; + return mRemoteAnimationDefinition != null + ? mRemoteAnimationDefinition.getAdapter(transit, activityTypes) + : null; } /** * Overrides the pending transition with the remote animation defined for the transition in the * set of defined remote animations in the app window token. */ - private void overrideWithRemoteAnimationIfSet(ActivityRecord animLpActivity, + private void overrideWithRemoteAnimationIfSet(@Nullable ActivityRecord animLpActivity, @TransitionOldType int transit, ArraySet<Integer> activityTypes) { if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) { // The crash transition has higher priority than any involved remote animations. return; } - if (animLpActivity == null) { - return; - } final RemoteAnimationAdapter adapter = getRemoteAnimationOverride(animLpActivity, transit, activityTypes); if (adapter != null) { - animLpActivity.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote( - adapter); + mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 4b735c2c218e..c44f4e3cbd71 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4436,9 +4436,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } boolean okToDisplay(boolean ignoreFrozen) { + return okToDisplay(ignoreFrozen, false /* ignoreScreenOn */); + } + + boolean okToDisplay(boolean ignoreFrozen, boolean ignoreScreenOn) { if (mDisplayId == DEFAULT_DISPLAY) { return (!mWmService.mDisplayFrozen || ignoreFrozen) - && mWmService.mDisplayEnabled && mWmService.mPolicy.isScreenOn(); + && mWmService.mDisplayEnabled + && (ignoreScreenOn || mWmService.mPolicy.isScreenOn()); } return mDisplayInfo.state == Display.STATE_ON; } @@ -4448,8 +4453,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } boolean okToAnimate(boolean ignoreFrozen) { - return okToDisplay(ignoreFrozen) && - (mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate()); + return okToAnimate(ignoreFrozen, false /* ignoreScreenOn */); + } + + boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) { + return okToDisplay(ignoreFrozen, ignoreScreenOn) + && (mDisplayId != DEFAULT_DISPLAY + || mWmService.mPolicy.okToAnimate(ignoreScreenOn)); } static final class TaskForResizePointSearchResult { diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 9ff701cafd66..e1fc75e6fd9f 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -731,7 +731,7 @@ public class DisplayRotation { private RotationAnimationPair selectRotationAnimation() { // If the screen is off or non-interactive, force a jumpcut. final boolean forceJumpcut = !mDisplayPolicy.isScreenOnFully() - || !mService.mPolicy.okToAnimate(); + || !mService.mPolicy.okToAnimate(false /* ignoreScreenOn */); final WindowState topFullscreen = mDisplayPolicy.getTopFullscreenOpaqueWindow(); if (DEBUG_ANIM) Slog.i(TAG, "selectRotationAnimation topFullscreen=" + topFullscreen + " rotationAnimation=" diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 20216c3afcd4..8ff9fbaa3995 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -341,11 +341,10 @@ class KeyguardController { /** * Called when occluded state changed. * - * @param currentTaskControllingOcclusion the task that controls the state whether keyguard - * should be occluded. That is the task to be shown on top of keyguard if it requests so. + * @param topActivity the activity that controls the state whether keyguard should + * be occluded. That is the activity to be shown on top of keyguard if it requests so. */ - private void handleOccludedChanged( - int displayId, @Nullable Task currentTaskControllingOcclusion) { + private void handleOccludedChanged(int displayId, @Nullable ActivityRecord topActivity) { // TODO(b/113840485): Handle app transition for individual display, and apply occluded // state change to secondary displays. // For now, only default display fully supports occluded change. Other displays only @@ -364,6 +363,13 @@ class KeyguardController { isDisplayOccluded(DEFAULT_DISPLAY) ? TRANSIT_KEYGUARD_OCCLUDE : TRANSIT_KEYGUARD_UNOCCLUDE); + // When the occluding activity also turns on the display, visibility of the activity + // can be committed before KEYGUARD_OCCLUDE transition is handled. + // Set mRequestForceTransition flag to make sure that the app transition animation + // is applied for such case. + if (topActivity != null) { + topActivity.mRequestForceTransition = true; + } updateKeyguardSleepToken(DEFAULT_DISPLAY); mWindowManager.executeAppTransition(); } finally { @@ -580,7 +586,7 @@ class KeyguardController { if (lastOccluded != mOccluded) { occludingChange = true; - controller.handleOccludedChanged(mDisplayId, task); + controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity); } if (occludingChange || turningScreenOn) { diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index f851e3559def..67bc7af6dc28 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -121,7 +121,10 @@ class RemoteAnimationController implements DeathRecipient { // Create the app targets final RemoteAnimationTarget[] appTargets = createAppAnimations(); - if (appTargets.length == 0) { + if (appTargets.length == 0 && !AppTransition.isKeyguardOccludeTransitOld(transit)) { + // Keyguard occlude transition can be executed before the occluding activity becomes + // visible. Even in this case, KeyguardService expects to receive binder call, so we + // don't cancel remote animation. ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate, mPendingAnimations=%d", mPendingAnimations.size()); @@ -133,12 +136,16 @@ class RemoteAnimationController implements DeathRecipient { // Create the remote wallpaper animation targets (if any) final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations(); - // TODO(bc-unlock): Create the remote non app animation targets (if any) + // Create the remote non app animation targets (if any) final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit); mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { try { linkToDeathOfRunner(); + ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): onAnimationStart," + + " transit=%s, apps=%d, wallpapers=%d, nonApps=%d", + AppTransition.appTransitionOldToString(transit), appTargets.length, + wallpaperTargets.length, nonAppTargets.length); mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets, wallpaperTargets, nonAppTargets, mFinishedCallback); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 070a7252a3bf..191c3a117e85 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2796,6 +2796,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return dc != null && dc.okToAnimate(ignoreFrozen); } + boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) { + final DisplayContent dc = getDisplayContent(); + return dc != null && dc.okToAnimate(ignoreFrozen, ignoreScreenOn); + } + @Override public void commitPendingTransaction() { scheduleAnimation(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 43dceeda3026..67edde140c04 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1183,7 +1183,8 @@ public class WindowManagerService extends IWindowManager.Stub final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm) { return main(context, im, showBootMsgs, onlyCore, policy, atm, - SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new); + new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new, + SurfaceControl.Builder::new); } /** @@ -1193,12 +1194,14 @@ public class WindowManagerService extends IWindowManager.Stub @VisibleForTesting public static WindowManagerService main(final Context context, final InputManagerService im, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, - ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory, + ActivityTaskManagerService atm, DisplayWindowSettingsProvider + displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, - atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0); + atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory, + surfaceControlFactory), 0); return sInstance; } @@ -1220,7 +1223,8 @@ public class WindowManagerService extends IWindowManager.Stub private WindowManagerService(Context context, InputManagerService inputManager, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, - ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory, + ActivityTaskManagerService atm, DisplayWindowSettingsProvider + displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { installLock(this, INDEX_WINDOW); @@ -1370,7 +1374,7 @@ public class WindowManagerService extends IWindowManager.Stub final String displaySettingsPath = Settings.Global.getString(resolver, DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); - mDisplayWindowSettingsProvider = new DisplayWindowSettingsProvider(); + mDisplayWindowSettingsProvider = displayWindowSettingsProvider; if (displaySettingsPath != null) { mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath); } @@ -1736,8 +1740,11 @@ public class WindowManagerService extends IWindowManager.Stub } // Switch to listen to the {@link WindowToken token}'s configuration changes when - // adding a window to the window context. - if (mWindowContextListenerController.hasListener(windowContextToken)) { + // adding a window to the window context. Filter sub window type here because the sub + // window must be attached to the parent window, which is attached to the window context + // created window token. + if (!win.isChildWindow() + && mWindowContextListenerController.hasListener(windowContextToken)) { final int windowContextType = mWindowContextListenerController .getWindowType(windowContextToken); if (type != windowContextType) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1ba57fd0251b..ddcb2bf7be22 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -173,6 +173,7 @@ import android.app.admin.FactoryResetProtectionPolicy; import android.app.admin.FullyManagedDeviceProvisioningParams; import android.app.admin.ManagedProfileProvisioningParams; import android.app.admin.NetworkEvent; +import android.app.admin.ParcelableGranteeMap; import android.app.admin.PasswordMetrics; import android.app.admin.PasswordPolicy; import android.app.admin.SecurityLog; @@ -278,6 +279,7 @@ import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.text.TextUtils; import android.text.format.DateUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.IndentingPrintWriter; @@ -5649,41 +5651,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public List<String> getKeyPairGrants(String callerPackage, String alias) { + public ParcelableGranteeMap getKeyPairGrants(String callerPackage, String alias) { final CallerIdentity caller = getCallerIdentity(callerPackage); Preconditions.checkCallAuthorization(canManageCertificates(caller)); - return mInjector.binderWithCleanCallingIdentity(() -> { + final ArrayMap<Integer, Set<String>> result = new ArrayMap<>(); + mInjector.binderWithCleanCallingIdentity(() -> { try (KeyChainConnection keyChainConnection = KeyChain.bindAsUser(mContext, caller.getUserHandle())) { - final List<String> result = new ArrayList<>(); final int[] granteeUids = keyChainConnection.getService().getGrants(alias); final PackageManager pm = mInjector.getPackageManager(caller.getUserId()); - // TODO: Return Set<Set<String>> when AIDL supports it: b/136048684 - // Public API returns a set of sets, where each internal set contains all package - // names corresponding to the same UID. For now a set of sets is marshalled as a - // null-separated list. for (final int uid : granteeUids) { final String[] packages = pm.getPackagesForUid(uid); if (packages == null) { Slogf.wtf(LOG_TAG, "No packages found for uid " + uid); continue; } - if (!result.isEmpty()) { - result.add(null); - } - result.addAll(Arrays.asList(packages)); + result.put(uid, new ArraySet<String>(packages)); } - return result; } catch (RemoteException e) { Slogf.e(LOG_TAG, "Querying keypair grants", e); } catch (InterruptedException e) { Slogf.w(LOG_TAG, "Interrupted while querying keypair grants", e); Thread.currentThread().interrupt(); } - return Collections.emptyList(); }); + return new ParcelableGranteeMap(result); } /** @@ -8125,20 +8119,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " as device owner for user " + userId); return false; } - if (admin == null - || !isPackageInstalledForUser(admin.getPackageName(), userId)) { - throw new IllegalArgumentException("Invalid component " + admin - + " for device owner"); - } + Preconditions.checkArgument(admin != null); final CallerIdentity caller = getCallerIdentity(); synchronized (getLockObject()) { enforceCanSetDeviceOwnerLocked(caller, admin, userId); + Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId), + "Invalid component " + admin + " for device owner"); final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId); - if (activeAdmin == null - || getUserData(userId).mRemovingAdmins.contains(admin)) { - throw new IllegalArgumentException("Not active admin: " + admin); - } + Preconditions.checkArgument(activeAdmin != null && !getUserData( + userId).mRemovingAdmins.contains(admin), "Not active admin: " + admin); // Shutting down backup manager service permanently. toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 912b8cad952c..4c4c5821a2db 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -384,6 +384,7 @@ public final class SystemServer implements Dumpable { private static final String ROLE_SERVICE_CLASS = "com.android.role.RoleService"; private static final String GAME_MANAGER_SERVICE_CLASS = "com.android.server.app.GameManagerService$Lifecycle"; + private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -2637,6 +2638,12 @@ public final class SystemServer implements Dumpable { LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal()); t.traceEnd(); + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)) { + t.traceBegin("UwbService"); + mSystemServiceManager.startService(UWB_SERVICE_CLASS); + t.traceEnd(); + } + t.traceBegin("StartBootPhaseDeviceSpecificServicesReady"); mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); t.traceEnd(); diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java index 14c02d53efad..0a5a3bff3676 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationJavaUtil.java @@ -20,11 +20,14 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.PackageManager; +import android.content.pm.verify.domain.DomainOwner; import android.content.pm.verify.domain.DomainVerificationManager; import com.android.server.pm.verify.domain.DomainVerificationService; +import java.util.List; import java.util.Set; +import java.util.SortedSet; import java.util.UUID; /** @@ -58,4 +61,14 @@ class DomainVerificationJavaUtil { throws PackageManager.NameNotFoundException { return manager.setDomainVerificationUserSelection(domainSetId, domains, enabled); } + + static SortedSet<DomainOwner> getOwnersForDomain(@NonNull DomainVerificationManager manager, + @Nullable String domain) { + return manager.getOwnersForDomain(domain); + } + + static List<DomainOwner> getOwnersForDomain(@NonNull DomainVerificationService service, + @Nullable String domain, @UserIdInt int userId) { + return service.getOwnersForDomain(domain, userId); + } } diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt index 7fea4ee42317..3838f68fcf22 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt @@ -292,8 +292,17 @@ class DomainVerificationManagerApiTest { val manager0 = makeManager(service, 0) val manager1 = makeManager(service, 1) - assertThat(service.getOwnersForDomain(DOMAIN_1, 0)).isEmpty() - assertThat(manager0.getOwnersForDomain(DOMAIN_1)).isEmpty() + listOf(DOMAIN_1, "").forEach { + assertThat(service.getOwnersForDomain(it, 0)).isEmpty() + assertThat(manager0.getOwnersForDomain(it)).isEmpty() + } + + assertFailsWith(NullPointerException::class) { + DomainVerificationJavaUtil.getOwnersForDomain(service, null, 0) + } + assertFailsWith(NullPointerException::class) { + DomainVerificationJavaUtil.getOwnersForDomain(manager0, null) + } assertThat( service.setStatus( diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index edfc21d47767..7654093f9e7e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -2223,7 +2223,11 @@ public class AlarmManagerServiceTest { } @Test - public void minWindow() { + public void minWindowChangeEnabled() { + doReturn(true).when( + () -> CompatChanges.isChangeEnabled( + eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS), + anyString(), any(UserHandle.class))); final long minWindow = 73; setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); @@ -2239,6 +2243,26 @@ public class AlarmManagerServiceTest { } @Test + public void minWindowChangeDisabled() { + doReturn(false).when( + () -> CompatChanges.isChangeEnabled( + eq(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS), + anyString(), any(UserHandle.class))); + final long minWindow = 73; + setDeviceConfigLong(KEY_MIN_WINDOW, minWindow); + + // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC. + for (int window = 1; window <= minWindow; window++) { + final PendingIntent pi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null); + + assertEquals(1, mService.mAlarmStore.size()); + final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0); + assertEquals(window, a.windowLength); + } + } + + @Test public void denyListPackagesAdded() { mService.mConstants.EXACT_ALARM_DENY_LIST = new ArraySet<>(new String[]{"p1", "p2", "p3"}); setDeviceConfigString(KEY_EXACT_ALARM_DENY_LIST, "p2,p4,p5"); 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 924ad7f3f5a1..76f7e80a3412 100644 --- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java @@ -35,7 +35,6 @@ import android.os.Looper; import android.os.Message; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; -import android.util.ArrayMap; import android.util.LongSparseArray; import androidx.test.InstrumentationRegistry; @@ -68,7 +67,6 @@ public class BlobStoreManagerServiceTest { private File mBlobsDir; private LongSparseArray<BlobStoreSession> mUserSessions; - private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs; private static final String TEST_PKG1 = "com.example1"; private static final String TEST_PKG2 = "com.example2"; @@ -99,10 +97,8 @@ public class BlobStoreManagerServiceTest { mHandler = new TestHandler(Looper.getMainLooper()); mService = new BlobStoreManagerService(mContext, new TestInjector()); mUserSessions = new LongSparseArray<>(); - mUserBlobs = new ArrayMap<>(); mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId()); - mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId()); } @After @@ -147,7 +143,7 @@ public class BlobStoreManagerServiceTest { final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, blobHandle1, true /* hasLeases */); doReturn(true).when(blobMetadata1).isACommitter(TEST_PKG1, TEST_UID1); - mUserBlobs.put(blobHandle1, blobMetadata1); + addBlob(blobHandle1, blobMetadata1); final long blobId2 = 347; final File blobFile2 = mock(File.class); @@ -156,7 +152,7 @@ public class BlobStoreManagerServiceTest { final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, blobHandle2, false /* hasLeases */); doReturn(false).when(blobMetadata2).isACommitter(TEST_PKG1, TEST_UID1); - mUserBlobs.put(blobHandle2, blobMetadata2); + addBlob(blobHandle2, blobMetadata2); final long blobId3 = 49875; final File blobFile3 = mock(File.class); @@ -165,7 +161,7 @@ public class BlobStoreManagerServiceTest { final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, blobHandle3, true /* hasLeases */); doReturn(true).when(blobMetadata3).isACommitter(TEST_PKG1, TEST_UID1); - mUserBlobs.put(blobHandle3, blobMetadata3); + addBlob(blobHandle3, blobMetadata3); mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4, blobId1, blobId2, blobId3); @@ -197,10 +193,10 @@ public class BlobStoreManagerServiceTest { verify(blobMetadata2).destroy(); verify(blobMetadata3).destroy(); - assertThat(mUserBlobs.size()).isEqualTo(1); - assertThat(mUserBlobs.get(blobHandle1)).isNotNull(); - assertThat(mUserBlobs.get(blobHandle2)).isNull(); - assertThat(mUserBlobs.get(blobHandle3)).isNull(); + assertThat(mService.getBlobsCountForTest()).isEqualTo(1); + assertThat(mService.getBlobForTest(blobHandle1)).isNotNull(); + assertThat(mService.getBlobForTest(blobHandle2)).isNull(); + assertThat(mService.getBlobForTest(blobHandle3)).isNull(); assertThat(mService.getActiveIdsForTest()).containsExactly( sessionId2, sessionId3, blobId1); @@ -293,7 +289,7 @@ public class BlobStoreManagerServiceTest { "label1", System.currentTimeMillis() - 2000, "tag1"); final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, blobHandle1, true /* hasLeases */); - mUserBlobs.put(blobHandle1, blobMetadata1); + addBlob(blobHandle1, blobMetadata1); final long blobId2 = 78974; final File blobFile2 = mock(File.class); @@ -301,7 +297,7 @@ public class BlobStoreManagerServiceTest { "label2", System.currentTimeMillis() + 30000, "tag2"); final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, blobHandle2, true /* hasLeases */); - mUserBlobs.put(blobHandle2, blobMetadata2); + addBlob(blobHandle2, blobMetadata2); final long blobId3 = 97; final File blobFile3 = mock(File.class); @@ -309,7 +305,7 @@ public class BlobStoreManagerServiceTest { "label3", System.currentTimeMillis() + 4400000, "tag3"); final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, blobHandle3, false /* hasLeases */); - mUserBlobs.put(blobHandle3, blobMetadata3); + addBlob(blobHandle3, blobMetadata3); mService.addActiveIdsForTest(blobId1, blobId2, blobId3); @@ -321,8 +317,8 @@ public class BlobStoreManagerServiceTest { verify(blobMetadata2, never()).destroy(); verify(blobMetadata3).destroy(); - assertThat(mUserBlobs.size()).isEqualTo(1); - assertThat(mUserBlobs.get(blobHandle2)).isNotNull(); + assertThat(mService.getBlobsCountForTest()).isEqualTo(1); + assertThat(mService.getBlobForTest(blobHandle2)).isNotNull(); assertThat(mService.getActiveIdsForTest()).containsExactly(blobId2); assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3); @@ -336,21 +332,21 @@ public class BlobStoreManagerServiceTest { doReturn(size1).when(blobMetadata1).getSize(); doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG1, TEST_UID1); doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG2, TEST_UID2); - mUserBlobs.put(mock(BlobHandle.class), blobMetadata1); + addBlob(mock(BlobHandle.class), blobMetadata1); final BlobMetadata blobMetadata2 = mock(BlobMetadata.class); final long size2 = 89475; doReturn(size2).when(blobMetadata2).getSize(); doReturn(false).when(blobMetadata2).isALeasee(TEST_PKG1, TEST_UID1); doReturn(true).when(blobMetadata2).isALeasee(TEST_PKG2, TEST_UID2); - mUserBlobs.put(mock(BlobHandle.class), blobMetadata2); + addBlob(mock(BlobHandle.class), blobMetadata2); final BlobMetadata blobMetadata3 = mock(BlobMetadata.class); final long size3 = 328732; doReturn(size3).when(blobMetadata3).getSize(); doReturn(true).when(blobMetadata3).isALeasee(TEST_PKG1, TEST_UID1); doReturn(false).when(blobMetadata3).isALeasee(TEST_PKG2, TEST_UID2); - mUserBlobs.put(mock(BlobHandle.class), blobMetadata3); + addBlob(mock(BlobHandle.class), blobMetadata3); // Verify usage is calculated correctly assertThat(mService.getTotalUsageBytesLocked(TEST_UID1, TEST_PKG1)) @@ -388,6 +384,11 @@ public class BlobStoreManagerServiceTest { return blobMetadata; } + private void addBlob(BlobHandle blobHandle, BlobMetadata blobMetadata) { + doReturn(blobHandle).when(blobMetadata).getBlobHandle(); + mService.addBlobLocked(blobMetadata); + } + private class TestHandler extends Handler { TestHandler(Looper looper) { super(looper); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index d7fbd4913b2c..9d055e0d431f 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -37,6 +37,7 @@ android_test { "services.net", "services.people", "services.usage", + "services.uwb", "guava", "androidx.test.core", "androidx.test.ext.truth", diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java index 5bef877e2f39..e9b5b6243089 100644 --- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -184,6 +184,7 @@ public class BroadcastRecordTest { false /* callerInstantApp */, null /* resolvedType */, null /* requiredPermissions */, + null /* excludedPermissions */, 0 /* appOp */, null /* options */, new ArrayList<>(receivers), // Make a copy to not affect the original list. diff --git a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java index 3231f6204a12..6ca1102b23ef 100644 --- a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java +++ b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java @@ -18,6 +18,8 @@ package com.android.server.am; import static com.android.server.am.MeasuredEnergySnapshot.UNAVAILABLE; +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; @@ -224,15 +226,15 @@ public final class MeasuredEnergySnapshotTest { } @Test - public void testGetNumOtherOrdinals() { + public void testGetOtherOrdinalNames() { final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP); - assertEquals(3, snapshot.getNumOtherOrdinals()); + assertThat(snapshot.getOtherOrdinalNames()).asList().containsExactly("GPU", "HPU", "IPU"); } @Test - public void testGetNumOtherOrdinals_none() { + public void testGetOtherOrdinalNames_none() { final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP); - assertEquals(0, snapshot.getNumOtherOrdinals()); + assertEquals(0, snapshot.getOtherOrdinalNames().length); } private static EnergyConsumer createEnergyConsumer(int id, int ord, byte type, String name) { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index e322ce551372..96bab6119154 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -308,6 +308,8 @@ public class AuthSessionTest { componentInfo, type, false /* resetLockoutRequiresHardwareAuthToken */)); + + when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); } private void setupFace(int id, boolean confirmationAlwaysRequired, @@ -329,6 +331,6 @@ public class AuthSessionTest { } }); - when(mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); + when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index a5fbab519aaa..ec3bea33c8db 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -378,7 +378,7 @@ public class BiometricServiceTest { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); // Disabled in user settings receives onError - when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); waitForIdle(); @@ -389,7 +389,7 @@ public class BiometricServiceTest { // Enrolled, not disabled in settings, user requires confirmation in settings resetReceivers(); - when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); when(mBiometricService.mSettingObserver.getConfirmationAlwaysRequired( anyInt() /* modality */, anyInt() /* userId */)) .thenReturn(true); @@ -1197,7 +1197,7 @@ public class BiometricServiceTest { @Test public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); - when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true); // When only biometric is requested @@ -1322,6 +1322,8 @@ public class BiometricServiceTest { final int testId = 0; + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())) .thenReturn(true); when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true); @@ -1536,7 +1538,7 @@ public class BiometricServiceTest { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); - when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); if ((modality & BiometricAuthenticator.TYPE_FINGERPRINT) != 0) { when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())) @@ -1564,7 +1566,7 @@ public class BiometricServiceTest { mBiometricService = new BiometricService(mContext, mInjector); mBiometricService.onStart(); - when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); assertEquals(modalities.length, strengths.length); 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 78e2dee7acf8..73ec5b8d3522 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1304,6 +1304,16 @@ public class DevicePolicyManagerTest extends DpmTestBase { @Test public void testSetDeviceOwner_failures() throws Exception { // TODO Test more failure cases. Basically test all chacks in enforceCanSetDeviceOwner(). + // Package doesn't exist and caller is not system + assertExpectException(SecurityException.class, + /* messageRegex= */ "Calling identity is not authorized", + () -> dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)); + + // Package exists, but caller is not system + setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); + assertExpectException(SecurityException.class, + /* messageRegex= */ "Calling identity is not authorized", + () -> dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 39e06a3a362d..950b8a254007 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -25,6 +25,13 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import android.content.Context; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; @@ -494,4 +501,99 @@ public class HdmiCecLocalDeviceTvTest { ABORT_UNRECOGNIZED_OPCODE); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbort); } + + @Test + public void handleReportAudioStatus_SamOnArcOff_setStreamVolumeNotCalled() { + // Emulate Audio device on port 0x1000 (does not support ARC) + mNativeWrapper.setPortConnectionStatus(1, true); + HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(hdmiCecMessage); + + HdmiCecFeatureAction systemAudioAutoInitiationAction = + new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM); + mHdmiCecLocalDeviceTv.addAndStartAction(systemAudioAutoInitiationAction); + HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode( + ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true); + mHdmiControlService.handleCecCommand(reportSystemAudioMode); + + mTestLooper.dispatchAll(); + + // SAM must be on; ARC must be off + assertTrue(mHdmiCecLocalDeviceTv.isSystemAudioActivated()); + assertFalse(mHdmiCecLocalDeviceTv.isArcEstablished()); + + HdmiCecMessage reportAudioStatus = HdmiCecMessageBuilder.buildReportAudioStatus( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + 50, // Volume of incoming message does not affect HDMI-CEC logic + false); + mNativeWrapper.onCecMessage(reportAudioStatus); + + mTestLooper.dispatchAll(); + + verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + } + + @Test + public void handleReportAudioStatus_SamOnArcOn_setStreamVolumeCalled() { + mNativeWrapper.setPortConnectionStatus(2, true); + HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x2000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(hdmiCecMessage); + + HdmiCecFeatureAction systemAudioAutoInitiationAction = + new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM); + mHdmiCecLocalDeviceTv.addAndStartAction(systemAudioAutoInitiationAction); + + HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode( + ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true); + mHdmiControlService.handleCecCommand(reportSystemAudioMode); + + HdmiCecMessage requestArcInitiation = HdmiCecMessageBuilder.buildInitiateArc( + ADDR_AUDIO_SYSTEM, + ADDR_TV); + mNativeWrapper.onCecMessage(requestArcInitiation); + + mTestLooper.dispatchAll(); + + // SAM and ARC must be on + assertTrue(mHdmiCecLocalDeviceTv.isSystemAudioActivated()); + assertTrue(mHdmiCecLocalDeviceTv.isArcEstablished()); + + HdmiCecMessage reportAudioStatus = HdmiCecMessageBuilder.buildReportAudioStatus( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + 50, // Volume of incoming message does not affect HDMI-CEC logic + false); + mNativeWrapper.onCecMessage(reportAudioStatus); + + mTestLooper.dispatchAll(); + + verify(mAudioManager, times(1)).setStreamVolume(anyInt(), anyInt(), anyInt()); + } + + @Test + public void handleReportAudioStatus_SamOff_setStreamVolumeNotCalled() { + // Emulate Audio device on port 0x1000 (does not support ARC) + mNativeWrapper.setPortConnectionStatus(1, true); + HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + mNativeWrapper.onCecMessage(hdmiCecMessage); + + mTestLooper.dispatchAll(); + + assertFalse(mHdmiCecLocalDeviceTv.isSystemAudioActivated()); + + HdmiCecMessage reportAudioStatus = HdmiCecMessageBuilder.buildReportAudioStatus( + ADDR_AUDIO_SYSTEM, + ADDR_TV, + 50, // Volume of incoming message does not affect HDMI-CEC logic + false); + mNativeWrapper.onCecMessage(reportAudioStatus); + + mTestLooper.dispatchAll(); + + verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt()); + } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java index 691d174f55f8..f2bb1d662ed9 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java @@ -87,6 +87,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { } @Override + String getRebootEscrowFile(int userId) { + return makeDirs(mStorageDir, super.getRebootEscrowFile(userId)).getAbsolutePath(); + } + + @Override protected File getSyntheticPasswordDirectoryForUser(int userId) { return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser( userId).getAbsolutePath()); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index 8c08226201a8..49a54ec1354b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -21,6 +21,11 @@ import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_STORE_ESCROW_KEY; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -327,7 +332,7 @@ public class RebootEscrowManagerTests { assertNull( mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); assertNotNull( mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); verify(mRebootEscrow).storeKey(any()); @@ -351,7 +356,7 @@ public class RebootEscrowManagerTests { when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); @@ -373,7 +378,7 @@ public class RebootEscrowManagerTests { assertNull( mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); doThrow(ServiceSpecificException.class).when(mRebootEscrow).storeKey(any()); - assertFalse(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_STORE_ESCROW_KEY, mService.armRebootEscrowIfNeeded()); verify(mRebootEscrow).storeKey(any()); } @@ -396,7 +401,7 @@ public class RebootEscrowManagerTests { assertNull( mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); assertNotNull( mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); verify(mRebootEscrow, times(1)).storeKey(any()); @@ -408,15 +413,24 @@ public class RebootEscrowManagerTests { @Test public void armService_NoInitialization_Failure() throws Exception { - assertFalse(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_ESCROW_NOT_READY, mService.armRebootEscrowIfNeeded()); verifyNoMoreInteractions(mRebootEscrow); } @Test public void armService_RebootEscrowServiceException_Failure() throws Exception { + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mRebootEscrow); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mRebootEscrow, never()).storeKey(any()); + doThrow(RemoteException.class).when(mRebootEscrow).storeKey(any()); - assertFalse(mService.armRebootEscrowIfNeeded()); - verifyNoMoreInteractions(mRebootEscrow); + assertEquals(ARM_REBOOT_ERROR_STORE_ESCROW_KEY, mService.armRebootEscrowIfNeeded()); + verify(mRebootEscrow).storeKey(any()); } @Test @@ -439,7 +453,7 @@ public class RebootEscrowManagerTests { verify(mRebootEscrow, never()).storeKey(any()); ArgumentCaptor<byte[]> keyByteCaptor = ArgumentCaptor.forClass(byte[].class); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mRebootEscrow).storeKey(keyByteCaptor.capture()); verify(mKeyStoreManager).getKeyStoreEncryptionKey(); @@ -483,7 +497,7 @@ public class RebootEscrowManagerTests { // Use x -> x for both wrap & unwrap functions. when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); assertTrue(mStorage.hasRebootEscrowServerBlob()); @@ -520,7 +534,7 @@ public class RebootEscrowManagerTests { // Use x -> x for both wrap & unwrap functions. when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); assertTrue(mStorage.hasRebootEscrowServerBlob()); @@ -557,7 +571,7 @@ public class RebootEscrowManagerTests { // Use x -> x for both wrap & unwrap functions. when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); assertTrue(mStorage.hasRebootEscrowServerBlob()); @@ -597,7 +611,7 @@ public class RebootEscrowManagerTests { // Use x -> x for both wrap & unwrap functions. when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) .thenAnswer(invocation -> invocation.getArgument(0)); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); assertTrue(mStorage.hasRebootEscrowServerBlob()); @@ -635,7 +649,7 @@ public class RebootEscrowManagerTests { verify(mRebootEscrow, never()).storeKey(any()); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mRebootEscrow).storeKey(any()); assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); @@ -695,7 +709,7 @@ public class RebootEscrowManagerTests { verify(mRebootEscrow, never()).storeKey(any()); ArgumentCaptor<byte[]> keyByteCaptor = ArgumentCaptor.forClass(byte[].class); - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mRebootEscrow).storeKey(keyByteCaptor.capture()); assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); @@ -732,8 +746,7 @@ public class RebootEscrowManagerTests { verify(mockListener).onPreparedForReboot(eq(true)); verify(mRebootEscrow, never()).storeKey(any()); - - assertTrue(mService.armRebootEscrowIfNeeded()); + assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded()); verify(mRebootEscrow).storeKey(any()); assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); @@ -756,4 +769,28 @@ public class RebootEscrowManagerTests { assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), metricsErrorCodeCaptor.getValue()); } + + @Test + public void armServiceProviderMismatch_Failure() throws Exception { + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mRebootEscrow); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); + verify(mRebootEscrow, never()).storeKey(any()); + + assertNull( + mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); + // Change the provider to server based, expect the reboot to fail + when(mInjected.forceServerBased()).thenReturn(true); + assertEquals(ARM_REBOOT_ERROR_PROVIDER_MISMATCH, mService.armRebootEscrowIfNeeded()); + assertNull( + mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_ARMED_KEY, null, USER_SYSTEM)); + // Verify that the escrow key & data have been cleared. + verify(mRebootEscrow).storeKey(eq(new byte[32])); + assertFalse(mStorage.hasRebootEscrow(PRIMARY_USER_ID)); + } } diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java index 2b358ea5e105..b64810b66542 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java @@ -18,11 +18,14 @@ package com.android.server.recoverysystem; import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME; import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED; +import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE; import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -31,6 +34,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -91,7 +95,8 @@ public class RecoverySystemServiceTest { mUncryptUpdateFileWriter = mock(FileWriter.class); mLockSettingsInternal = mock(LockSettingsInternal.class); - when(mLockSettingsInternal.armRebootEscrow()).thenReturn(true); + doReturn(LockSettingsInternal.ARM_REBOOT_ERROR_NONE).when(mLockSettingsInternal) + .armRebootEscrow(); Looper looper = InstrumentationRegistry.getContext().getMainLooper(); mIPowerManager = mock(IPowerManager.class); @@ -489,4 +494,27 @@ public class RecoverySystemServiceTest { } // TODO(xunchang) add more multi client tests + + @Test + public void rebootWithLskf_armEscrowDataFatalError_Failure() throws Exception { + doReturn(LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH) + .when(mLockSettingsInternal).armRebootEscrow(); + + assertTrue(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null)); + mRecoverySystemService.onPreparedForReboot(true); + assertTrue(mRecoverySystemService.isLskfCaptured(FAKE_OTA_PACKAGE_NAME)); + + when(mSharedPreferences.getInt(eq(FAKE_OTA_PACKAGE_NAME + + RecoverySystemService.REQUEST_LSKF_COUNT_PREF_SUFFIX), anyInt())).thenReturn(1); + when(mSharedPreferences.getInt(eq(RecoverySystemService.LSKF_CAPTURED_COUNT_PREF), + anyInt())).thenReturn(1); + assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE, + mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true)); + // Verify that the RoR preparation state has been cleared. + assertFalse(mRecoverySystemService.isLskfCaptured(FAKE_OTA_PACKAGE_NAME)); + verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(5004 /* provider mismatch */), + eq(1000), eq(1) /* client count */, eq(1) /* request count */, + eq(true) /* slot switch */, anyBoolean(), anyInt(), + eq(1) /* lskf capture count */); + } } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 742f5034a248..32fed3bc3dc1 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -248,21 +248,6 @@ public class TimeDetectorServiceTest { mStubbedTimeDetectorStrategy.verifyDumpCalled(); } - @Test - public void testAutoTimeDetectionToggle() throws Exception { - mTimeDetectorService.handleAutoTimeDetectionChanged(); - mTestHandler.assertTotalMessagesEnqueued(1); - mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled(); - - mStubbedTimeDetectorStrategy.resetCallTracking(); - - mTimeDetectorService.handleAutoTimeDetectionChanged(); - mTestHandler.assertTotalMessagesEnqueued(2); - mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled(); - } - private static TelephonyTimeSuggestion createTelephonyTimeSuggestion() { int slotIndex = 1234; TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); @@ -298,7 +283,6 @@ public class TimeDetectorServiceTest { private NetworkTimeSuggestion mLastNetworkSuggestion; private GnssTimeSuggestion mLastGnssSuggestion; private ExternalTimeSuggestion mLastExternalSuggestion; - private boolean mHandleAutoTimeDetectionChangedCalled; private boolean mDumpCalled; @Override @@ -333,11 +317,6 @@ public class TimeDetectorServiceTest { } @Override - public void handleAutoTimeConfigChanged() { - mHandleAutoTimeDetectionChangedCalled = true; - } - - @Override public void dump(IndentingPrintWriter pw, String[] args) { mDumpCalled = true; } @@ -348,7 +327,6 @@ public class TimeDetectorServiceTest { mLastNetworkSuggestion = null; mLastGnssSuggestion = null; mLastExternalSuggestion = null; - mHandleAutoTimeDetectionChangedCalled = false; mDumpCalled = false; } @@ -372,10 +350,6 @@ public class TimeDetectorServiceTest { assertEquals(expectedSuggestion, mLastExternalSuggestion); } - void verifyHandleAutoTimeDetectionChangedCalled() { - assertTrue(mHandleAutoTimeDetectionChangedCalled); - } - void verifyDumpCalled() { assertTrue(mDumpCalled); } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 095703e6b939..0d5b5a565d5a 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -37,6 +37,7 @@ import android.os.TimestampedValue; import androidx.test.runner.AndroidJUnit4; import com.android.server.timedetector.TimeDetectorStrategy.Origin; +import com.android.server.timezonedetector.ConfigurationChangeListener; import org.junit.Before; import org.junit.Test; @@ -46,6 +47,7 @@ import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; @RunWith(AndroidJUnit4.class) public class TimeDetectorStrategyImplTest { @@ -1133,11 +1135,17 @@ public class TimeDetectorStrategyImplTest { private long mSystemClockMillis; private int mSystemClockUpdateThresholdMillis = 2000; private int[] mAutoOriginPriorities = PROVIDERS_PRIORITY; + private ConfigurationChangeListener mConfigChangeListener; // Tracking operations. private boolean mSystemClockWasSet; @Override + public void setConfigChangeListener(ConfigurationChangeListener listener) { + mConfigChangeListener = Objects.requireNonNull(listener); + } + + @Override public int systemClockUpdateThresholdMillis() { return mSystemClockUpdateThresholdMillis; } @@ -1230,6 +1238,7 @@ public class TimeDetectorStrategyImplTest { void simulateAutoTimeZoneDetectionToggle() { mAutoTimeDetectionEnabled = !mAutoTimeDetectionEnabled; + mConfigChangeListener.onChange(); } void verifySystemClockNotSet() { @@ -1330,7 +1339,6 @@ public class TimeDetectorStrategyImplTest { Script simulateAutoTimeDetectionToggle() { mFakeEnvironment.simulateAutoTimeZoneDetectionToggle(); - mTimeDetectorStrategy.handleAutoTimeConfigChanged(); return this; } diff --git a/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java b/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java new file mode 100644 index 000000000000..2f5a5cc97117 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/uwb/UwbServiceImplTest.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2021 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.uwb; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.IBinder; +import android.os.PersistableBundle; +import android.platform.test.annotations.Presubmit; +import android.test.suitebuilder.annotation.SmallTest; +import android.uwb.IUwbAdapter; +import android.uwb.IUwbAdapterStateCallbacks; +import android.uwb.IUwbRangingCallbacks; +import android.uwb.RangingReport; +import android.uwb.RangingSession; +import android.uwb.SessionHandle; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +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; + +/** + * Tests for {@link UwbServiceImpl}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class UwbServiceImplTest { + @Mock private IUwbAdapter mVendorService; + @Mock private IBinder mVendorServiceBinder; + @Mock private Context mContext; + @Mock private UwbInjector mUwbInjector; + @Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor; + @Captor private ArgumentCaptor<IBinder.DeathRecipient> mClientDeathCaptor; + @Captor private ArgumentCaptor<IBinder.DeathRecipient> mVendorServiceDeathCaptor; + + private UwbServiceImpl mUwbServiceImpl; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + when(mUwbInjector.getVendorService()).thenReturn(mVendorService); + when(mVendorService.asBinder()).thenReturn(mVendorServiceBinder); + mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector); + } + + @Test + public void testApiCallThrowsIllegalStateExceptionIfVendorServiceNotFound() throws Exception { + when(mUwbInjector.getVendorService()).thenReturn(null); + + final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class); + try { + mUwbServiceImpl.registerAdapterStateCallbacks(cb); + fail(); + } catch (IllegalStateException e) { /* pass */ } + } + + @Test + public void testRegisterAdapterStateCallbacks() throws Exception { + final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class); + mUwbServiceImpl.registerAdapterStateCallbacks(cb); + + verify(mVendorService).registerAdapterStateCallbacks(cb); + } + + @Test + public void testUnregisterAdapterStateCallbacks() throws Exception { + final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class); + mUwbServiceImpl.unregisterAdapterStateCallbacks(cb); + + verify(mVendorService).unregisterAdapterStateCallbacks(cb); + } + + @Test + public void testGetTimestampResolutionNanos() throws Exception { + final long timestamp = 34L; + when(mVendorService.getTimestampResolutionNanos()).thenReturn(timestamp); + assertThat(mUwbServiceImpl.getTimestampResolutionNanos()).isEqualTo(timestamp); + + verify(mVendorService).getTimestampResolutionNanos(); + } + + @Test + public void testGetSpecificationInfo() throws Exception { + final PersistableBundle specification = new PersistableBundle(); + when(mVendorService.getSpecificationInfo()).thenReturn(specification); + assertThat(mUwbServiceImpl.getSpecificationInfo()).isEqualTo(specification); + + verify(mVendorService).getSpecificationInfo(); + } + + @Test + public void testOpenRanging() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class); + final PersistableBundle parameters = new PersistableBundle(); + final IBinder cbBinder = mock(IBinder.class); + when(cb.asBinder()).thenReturn(cbBinder); + + mUwbServiceImpl.openRanging(sessionHandle, cb, parameters); + + verify(mVendorService).openRanging( + eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters)); + assertThat(mRangingCbCaptor.getValue()).isNotNull(); + } + + @Test + public void testStartRanging() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + final PersistableBundle parameters = new PersistableBundle(); + + mUwbServiceImpl.startRanging(sessionHandle, parameters); + + verify(mVendorService).startRanging(sessionHandle, parameters); + } + + @Test + public void testReconfigureRanging() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + final PersistableBundle parameters = new PersistableBundle(); + + mUwbServiceImpl.reconfigureRanging(sessionHandle, parameters); + + verify(mVendorService).reconfigureRanging(sessionHandle, parameters); + } + + @Test + public void testStopRanging() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + + mUwbServiceImpl.stopRanging(sessionHandle); + + verify(mVendorService).stopRanging(sessionHandle); + } + + @Test + public void testCloseRanging() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + + mUwbServiceImpl.closeRanging(sessionHandle); + + verify(mVendorService).closeRanging(sessionHandle); + } + + @Test + public void testRangingCallbacks() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class); + final PersistableBundle parameters = new PersistableBundle(); + final IBinder cbBinder = mock(IBinder.class); + when(cb.asBinder()).thenReturn(cbBinder); + + mUwbServiceImpl.openRanging(sessionHandle, cb, parameters); + + verify(mVendorService).openRanging( + eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters)); + assertThat(mRangingCbCaptor.getValue()).isNotNull(); + + // Invoke vendor service callbacks and ensure that the corresponding app callback is + // invoked. + mRangingCbCaptor.getValue().onRangingOpened(sessionHandle); + verify(cb).onRangingOpened(sessionHandle); + + mRangingCbCaptor.getValue().onRangingOpenFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + verify(cb).onRangingOpenFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + + mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters); + verify(cb).onRangingStarted(sessionHandle, parameters); + + mRangingCbCaptor.getValue().onRangingStartFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + verify(cb).onRangingStartFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + + mRangingCbCaptor.getValue().onRangingReconfigured(sessionHandle, parameters); + verify(cb).onRangingReconfigured(sessionHandle, parameters); + + mRangingCbCaptor.getValue().onRangingReconfigureFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + verify(cb).onRangingReconfigureFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + + mRangingCbCaptor.getValue().onRangingStopped( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + verify(cb).onRangingStopped( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + + mRangingCbCaptor.getValue().onRangingStopFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + verify(cb).onRangingStopFailed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + + final RangingReport rangingReport = new RangingReport.Builder().build(); + mRangingCbCaptor.getValue().onRangingResult(sessionHandle, rangingReport); + verify(cb).onRangingResult(sessionHandle, rangingReport); + + mRangingCbCaptor.getValue().onRangingClosed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + verify(cb).onRangingClosed( + sessionHandle, RangingSession.Callback.REASON_GENERIC_ERROR, parameters); + } + + @Test + public void testHandleClientDeath() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class); + final PersistableBundle parameters = new PersistableBundle(); + final IBinder cbBinder = mock(IBinder.class); + when(cb.asBinder()).thenReturn(cbBinder); + + mUwbServiceImpl.openRanging(sessionHandle, cb, parameters); + + verify(mVendorService).openRanging( + eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters)); + assertThat(mRangingCbCaptor.getValue()).isNotNull(); + + verify(cbBinder).linkToDeath(mClientDeathCaptor.capture(), anyInt()); + assertThat(mClientDeathCaptor.getValue()).isNotNull(); + + clearInvocations(cb); + + // Invoke cb, ensure it reaches the client. + mRangingCbCaptor.getValue().onRangingOpened(sessionHandle); + verify(cb).onRangingOpened(sessionHandle); + + // Trigger client death and ensure the session is stopped. + mClientDeathCaptor.getValue().binderDied(); + verify(mVendorService).stopRanging(sessionHandle); + verify(mVendorService).closeRanging(sessionHandle); + + // Invoke cb, it should be ignored. + mRangingCbCaptor.getValue().onRangingStarted(sessionHandle, parameters); + verify(cb, never()).onRangingStarted(any(), any()); + } + + @Test + public void testHandleVendorServiceDeath() throws Exception { + final SessionHandle sessionHandle = new SessionHandle(5); + final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class); + final PersistableBundle parameters = new PersistableBundle(); + final IBinder cbBinder = mock(IBinder.class); + when(cb.asBinder()).thenReturn(cbBinder); + + mUwbServiceImpl.openRanging(sessionHandle, cb, parameters); + + verify(mVendorServiceBinder).linkToDeath(mVendorServiceDeathCaptor.capture(), anyInt()); + assertThat(mVendorServiceDeathCaptor.getValue()).isNotNull(); + + verify(mVendorService).openRanging( + eq(sessionHandle), mRangingCbCaptor.capture(), eq(parameters)); + assertThat(mRangingCbCaptor.getValue()).isNotNull(); + + clearInvocations(cb); + + // Invoke cb, ensure it reaches the client. + mRangingCbCaptor.getValue().onRangingOpened(sessionHandle); + verify(cb).onRangingOpened(sessionHandle); + + // Trigger vendor service death and ensure that the client is informed of session end. + mVendorServiceDeathCaptor.getValue().binderDied(); + verify(cb).onRangingClosed( + eq(sessionHandle), eq(RangingSession.Callback.REASON_UNKNOWN), + argThat((p) -> p.isEmpty())); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 678defe36566..57cf865268ef 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -23,17 +23,27 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doCallRealMethod; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationDefinition; +import android.view.RemoteAnimationTarget; import android.view.WindowManager; import androidx.test.filters.FlakyTest; @@ -426,4 +436,104 @@ public class AppTransitionControllerTest extends WindowTestsBase { AppTransitionController.getAnimationTargets( opening, closing, false /* visible */)); } -} + + static class TestRemoteAnimationRunner implements IRemoteAnimationRunner { + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + } + + @Override + public void onAnimationCancelled() throws RemoteException { + } + + @Override + public IBinder asBinder() { + return new Binder(); + } + } + + @Test + public void testGetRemoteAnimationOverrideEmpty() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + assertNull(mAppTransitionController.getRemoteAnimationOverride(activity, + TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>())); + } + + @Test + public void testGetRemoteAnimationOverrideWindowContainer() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter); + activity.registerRemoteAnimations(definition); + + assertEquals(adapter, + mAppTransitionController.getRemoteAnimationOverride( + activity, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>())); + assertNull(mAppTransitionController.getRemoteAnimationOverride( + null, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>())); + } + + @Test + public void testGetRemoteAnimationOverrideTransitionController() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter); + mAppTransitionController.registerRemoteAnimations(definition); + + assertEquals(adapter, + mAppTransitionController.getRemoteAnimationOverride( + activity, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>())); + assertEquals(adapter, + mAppTransitionController.getRemoteAnimationOverride( + null, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>())); + } + + @Test + public void testGetRemoteAnimationOverrideBoth() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final RemoteAnimationDefinition definition1 = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter adapter1 = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + definition1.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter1); + activity.registerRemoteAnimations(definition1); + + final RemoteAnimationDefinition definition2 = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter adapter2 = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + definition2.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, adapter2); + mAppTransitionController.registerRemoteAnimations(definition2); + + assertEquals(adapter2, + mAppTransitionController.getRemoteAnimationOverride( + activity, TRANSIT_OLD_KEYGUARD_UNOCCLUDE, new ArraySet<Integer>())); + assertEquals(adapter2, + mAppTransitionController.getRemoteAnimationOverride( + null, TRANSIT_OLD_KEYGUARD_UNOCCLUDE, new ArraySet<Integer>())); + } + + @Test + public void testGetRemoteAnimationOverrideWindowContainerHasPriority() { + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final RemoteAnimationDefinition definition1 = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter adapter1 = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + definition1.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter1); + activity.registerRemoteAnimations(definition1); + + final RemoteAnimationDefinition definition2 = new RemoteAnimationDefinition(); + final RemoteAnimationAdapter adapter2 = new RemoteAnimationAdapter( + new TestRemoteAnimationRunner(), 10, 1); + definition2.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter2); + mAppTransitionController.registerRemoteAnimations(definition2); + + assertEquals(adapter1, + mAppTransitionController.getRemoteAnimationOverride( + activity, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>())); + } +}
\ No newline at end of file diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index afaf1b1fea0a..491fe98660c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -110,6 +110,7 @@ public class SystemServicesTestRule implements TestRule { private ActivityTaskManagerService mAtmService; private WindowManagerService mWmService; private TestWindowManagerPolicy mWMPolicy; + private TestDisplayWindowSettingsProvider mTestDisplayWindowSettingsProvider; private WindowState.PowerManagerWrapper mPowerManagerWrapper; private InputManagerService mImService; private InputChannel mInputChannel; @@ -284,10 +285,12 @@ public class SystemServicesTestRule implements TestRule { mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); mWMPolicy = new TestWindowManagerPolicy(this::getWindowManagerService, mPowerManagerWrapper); + mTestDisplayWindowSettingsProvider = new TestDisplayWindowSettingsProvider(); // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood. DisplayThread.getHandler().post(StrictMode::allowThreadDiskWritesMask); mWmService = WindowManagerService.main( - mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new, + mContext, mImService, false, false, mWMPolicy, mAtmService, + mTestDisplayWindowSettingsProvider, StubTransaction::new, () -> mSurfaceFactory.get(), (unused) -> new MockSurfaceControlBuilder()); spyOn(mWmService); spyOn(mWmService.mRoot); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index 777149b60098..ce2d74859931 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -147,10 +147,6 @@ class TestDisplayContent extends DisplayContent { final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, mInfo, DEFAULT_DISPLAY_ADJUSTMENTS); final TestDisplayContent newDisplay = createInternal(display); - // Ensure letterbox aspect ratio is not overridden on any device target. - // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by - // the below method, is set on some device form factors. - mService.mWindowManager.setFixedOrientationLetterboxAspectRatio(0); // disable the normal system decorations final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy(); spyOn(displayPolicy); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java new file mode 100644 index 000000000000..b2e44b1421f4 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 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.wm; + +import android.annotation.NonNull; +import android.view.DisplayInfo; + +import java.util.HashMap; +import java.util.Map; + +/** + * In-memory DisplayWindowSettingsProvider used in tests. Ensures no settings are read from or + * written to device-specific display settings files. + */ +public final class TestDisplayWindowSettingsProvider extends DisplayWindowSettingsProvider { + + private final Map<String, SettingsEntry> mOverrideSettingsMap = new HashMap<>(); + + @Override + @NonNull + public SettingsEntry getSettings(@NonNull DisplayInfo info) { + // Because no settings are read from settings files, there is no need to store base + // settings. Only override settings are necessary to track because they can be modified + // during tests (e.g. display size, ignore orientation requests). + return getOverrideSettings(info); + } + + @Override + @NonNull + public SettingsEntry getOverrideSettings(@NonNull DisplayInfo info) { + return new SettingsEntry(getOrCreateOverrideSettingsEntry(info)); + } + + @Override + public void updateOverrideSettings(@NonNull DisplayInfo info, + @NonNull SettingsEntry overrides) { + final SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); + overrideSettings.setTo(overrides); + } + + @NonNull + private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + if ((settings = mOverrideSettingsMap.get(identifier)) != null) { + return settings; + } + settings = new SettingsEntry(); + mOverrideSettingsMap.put(identifier, settings); + return settings; + } + + /** + * In {@link TestDisplayWindowSettingsProvider}, always use uniqueId as the identifier. + */ + private static String getIdentifier(DisplayInfo displayInfo) { + return displayInfo.uniqueId; + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index ae8e2ded359d..acadb74d333f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -210,7 +210,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public boolean okToAnimate() { + public boolean okToAnimate(boolean ignoreScreenOn) { return mOkToAnimate; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index dd0c9e6d390e..d9aa871447be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -50,7 +51,13 @@ import android.content.pm.PackageManager; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.view.IWindowSessionCallback; +import android.view.InsetsSourceControl; +import android.view.InsetsState; +import android.view.View; +import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -250,4 +257,31 @@ public class WindowManagerServiceTests extends WindowTestsBase { eq(clientToken), eq(windowToken), anyInt(), eq(TYPE_INPUT_METHOD), eq(windowToken.mOptions)); } + + @Test + public void testAddWindowWithSubWindowTypeByWindowContext() { + spyOn(mWm.mWindowContextListenerController); + + final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay); + final Session session = new Session(mWm, new IWindowSessionCallback.Stub() { + @Override + public void onAnimatorScaleChanged(float v) throws RemoteException {} + }); + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + TYPE_APPLICATION_ATTACHED_DIALOG); + params.token = windowToken.token; + final IBinder windowContextToken = new Binder(); + params.setWindowContextToken(windowContextToken); + doReturn(true).when(mWm.mWindowContextListenerController) + .hasListener(eq(windowContextToken)); + doReturn(TYPE_INPUT_METHOD).when(mWm.mWindowContextListenerController) + .getWindowType(eq(windowContextToken)); + + mWm.addWindow(session, new TestIWindow(), params, View.VISIBLE, DEFAULT_DISPLAY, + UserHandle.USER_SYSTEM, new InsetsState(), null, new InsetsState(), + new InsetsSourceControl[0]); + + verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(), + any(), anyInt(), anyInt(), any()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 39fdb2d48a70..5bafbbd2bdf7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -55,6 +55,8 @@ import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; @@ -153,6 +155,22 @@ class WindowTestsBase extends SystemServiceTestsBase { */ Transaction mTransaction; + /** + * Whether device-specific global overrides have already been checked in + * {@link WindowTestsBase#setUpBase()}. + */ + private static boolean sGlobalOverridesChecked; + /** + * Whether device-specific overrides have already been checked in + * {@link WindowTestsBase#setUpBase()} when the default display is used. + */ + private static boolean sOverridesCheckedDefaultDisplay; + /** + * Whether device-specific overrides have already been checked in + * {@link WindowTestsBase#setUpBase()} when a {@link TestDisplayContent} is used. + */ + private static boolean sOverridesCheckedTestDisplay; + @BeforeClass public static void setUpOnceBase() { AttributeCache.init(getInstrumentation().getTargetContext()); @@ -190,6 +208,28 @@ class WindowTestsBase extends SystemServiceTestsBase { // {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set // on some device form factors. mAtm.mWindowManager.setFixedOrientationLetterboxAspectRatio(0); + + checkDeviceSpecificOverridesNotApplied(); + } + + /** + * Check that device-specific overrides are not applied. Only need to check once during entire + * test run for each case: global overrides, default display, and test display. + */ + private void checkDeviceSpecificOverridesNotApplied() { + // Check global overrides + if (!sGlobalOverridesChecked) { + assertEquals(0, mWm.getFixedOrientationLetterboxAspectRatio(), 0 /* delta */); + sGlobalOverridesChecked = true; + } + // Check display-specific overrides + if (!sOverridesCheckedDefaultDisplay && mDisplayContent == mDefaultDisplay) { + assertFalse(mDisplayContent.getIgnoreOrientationRequest()); + sOverridesCheckedDefaultDisplay = true; + } else if (!sOverridesCheckedTestDisplay && mDisplayContent instanceof TestDisplayContent) { + assertFalse(mDisplayContent.getIgnoreOrientationRequest()); + sOverridesCheckedTestDisplay = true; + } } private void createTestDisplay(UseTestDisplay annotation) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index a6b68e1e2ab2..309673d72dd4 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -24,6 +24,7 @@ import static android.app.usage.UsageEvents.Event.FLUSH_TO_DISK; import static android.app.usage.UsageEvents.Event.LOCUS_ID_SET; import static android.app.usage.UsageEvents.Event.NOTIFICATION_INTERRUPTION; import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION; +import static android.app.usage.UsageEvents.Event.USER_INTERACTION; import static android.app.usage.UsageEvents.Event.USER_STOPPED; import static android.app.usage.UsageEvents.Event.USER_UNLOCKED; import static android.app.usage.UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY; @@ -112,6 +113,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; @@ -1988,6 +1990,17 @@ public class UsageStatsService extends SystemService implements } @Override + public void reportUserInteraction(String packageName, int userId) { + Objects.requireNonNull(packageName); + if (!isCallingUidSystem()) { + throw new SecurityException("Only system is allowed to call reportUserInteraction"); + } + final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime()); + event.mPackage = packageName; + reportEventOrAddToQueue(userId, event); + } + + @Override public void registerAppUsageObserver(int observerId, String[] packages, long timeLimitMs, PendingIntent callbackIntent, String callingPackage) { diff --git a/services/uwb/Android.bp b/services/uwb/Android.bp new file mode 100644 index 000000000000..da30d43a4536 --- /dev/null +++ b/services/uwb/Android.bp @@ -0,0 +1,26 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "services.uwb-sources", + srcs: ["java/**/*.java"], + path: "java", + visibility: ["//frameworks/base/services"], +} + +java_library_static { + name: "services.uwb", + defaults: ["platform_service_defaults"], + srcs: [ + ":services.uwb-sources", + ], + libs: [ + "services.core", + ], +} diff --git a/services/uwb/java/com/android/server/uwb/UwbInjector.java b/services/uwb/java/com/android/server/uwb/UwbInjector.java new file mode 100644 index 000000000000..00c0acabcb3b --- /dev/null +++ b/services/uwb/java/com/android/server/uwb/UwbInjector.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2021 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.uwb; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; +import android.uwb.IUwbAdapter; + + +/** + * To be used for dependency injection (especially helps mocking static dependencies). + */ +public class UwbInjector { + private static final String TAG = "UwbInjector"; + + private static final String VENDOR_SERVICE_NAME = "uwb_vendor"; + + private final Context mContext; + + public UwbInjector(@NonNull Context context) { + mContext = context; + } + + /** + * @return Returns the vendor service handle. + */ + public IUwbAdapter getVendorService() { + IBinder b = ServiceManager.getService(VENDOR_SERVICE_NAME); + if (b == null) return null; + return IUwbAdapter.Stub.asInterface(b); + } +} diff --git a/services/uwb/java/com/android/server/uwb/UwbService.java b/services/uwb/java/com/android/server/uwb/UwbService.java new file mode 100644 index 000000000000..4bb280f75ed1 --- /dev/null +++ b/services/uwb/java/com/android/server/uwb/UwbService.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 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.uwb; + +import android.content.Context; +import android.util.Log; + +import com.android.server.SystemService; + +/** + * Uwb System service. + */ +public class UwbService extends SystemService { + private static final String TAG = "UwbService"; + + private final UwbServiceImpl mImpl; + + public UwbService(Context context) { + super(context); + mImpl = new UwbServiceImpl(context, new UwbInjector(context)); + } + + @Override + public void onStart() { + Log.i(TAG, "Registering " + Context.UWB_SERVICE); + publishBinderService(Context.UWB_SERVICE, mImpl); + } +} diff --git a/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java new file mode 100644 index 000000000000..70bd20e0014e --- /dev/null +++ b/services/uwb/java/com/android/server/uwb/UwbServiceImpl.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2021 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.uwb; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.IBinder; +import android.os.PersistableBundle; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Log; +import android.uwb.IUwbAdapter; +import android.uwb.IUwbAdapterStateCallbacks; +import android.uwb.IUwbRangingCallbacks; +import android.uwb.RangingReport; +import android.uwb.RangingSession; +import android.uwb.SessionHandle; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Map; + +/** + * Implementation of {@link android.uwb.IUwbAdapter} binder service. + */ +public class UwbServiceImpl extends IUwbAdapter.Stub implements IBinder.DeathRecipient{ + private static final String TAG = "UwbServiceImpl"; + + private final Context mContext; + private final UwbInjector mUwbInjector; + /** + * Map for storing the callbacks wrapper for each session. + */ + @GuardedBy("mCallbacksMap") + private final Map<SessionHandle, UwbRangingCallbacksWrapper> mCallbacksMap = new ArrayMap<>(); + + /** + * Used for caching the vendor implementation of {@link IUwbAdapter} interface. + */ + private IUwbAdapter mVendorUwbAdapter; + + /** + * Wrapper for callback registered with vendor service. This wrapper is needed for performing + * permission check before sending the callback to the external app. + * + * Access to these callbacks are synchronized. + */ + private class UwbRangingCallbacksWrapper extends IUwbRangingCallbacks.Stub + implements IBinder.DeathRecipient{ + private final SessionHandle mSessionHandle; + private final IUwbRangingCallbacks mExternalCb; + private boolean mIsValid; + + UwbRangingCallbacksWrapper(@NonNull SessionHandle sessionHandle, + @NonNull IUwbRangingCallbacks externalCb) { + mSessionHandle = sessionHandle; + mExternalCb = externalCb; + mIsValid = true; + + // Link to death for external callback. + linkToDeath(); + } + + private void linkToDeath() { + IBinder binder = mExternalCb.asBinder(); + try { + binder.linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(TAG, "Unable to link to client death event.", e); + } + } + + private void removeClientAndUnlinkToDeath() { + // Remove from the map. + synchronized (mCallbacksMap) { + mCallbacksMap.remove(mSessionHandle); + } + IBinder binder = mExternalCb.asBinder(); + binder.unlinkToDeath(this, 0); + mIsValid = false; + } + + + @Override + public synchronized void onRangingOpened(SessionHandle sessionHandle) + throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingOpened(sessionHandle); + } + + @Override + public synchronized void onRangingOpenFailed(SessionHandle sessionHandle, + int reason, PersistableBundle parameters) throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingOpenFailed(sessionHandle, reason, parameters); + } + + @Override + public synchronized void onRangingStarted(SessionHandle sessionHandle, + PersistableBundle parameters) + throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingStarted(sessionHandle, parameters); + } + + @Override + public synchronized void onRangingStartFailed(SessionHandle sessionHandle, + int reason, PersistableBundle parameters) throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingStartFailed(sessionHandle, reason, parameters); + } + + @Override + public synchronized void onRangingReconfigured(SessionHandle sessionHandle, + PersistableBundle parameters) + throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingReconfigured(sessionHandle, parameters); + } + + @Override + public synchronized void onRangingReconfigureFailed(SessionHandle sessionHandle, + int reason, PersistableBundle parameters) throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingReconfigureFailed(sessionHandle, reason, parameters); + } + + @Override + public synchronized void onRangingStopped(SessionHandle sessionHandle, int reason, + PersistableBundle parameters) + throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingStopped(sessionHandle, reason, parameters); + } + + @Override + public synchronized void onRangingStopFailed(SessionHandle sessionHandle, int reason, + PersistableBundle parameters) throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingStopFailed(sessionHandle, reason, parameters); + } + + @Override + public synchronized void onRangingClosed(SessionHandle sessionHandle, int reason, + PersistableBundle parameters) throws RemoteException { + if (!mIsValid) return; + mExternalCb.onRangingClosed(sessionHandle, reason, parameters); + removeClientAndUnlinkToDeath(); + } + + @Override + public synchronized void onRangingResult(SessionHandle sessionHandle, + RangingReport rangingReport) + throws RemoteException { + if (!mIsValid) return; + // TODO: Perform permission checks and noteOp. + mExternalCb.onRangingResult(sessionHandle, rangingReport); + } + + @Override + public synchronized void binderDied() { + if (!mIsValid) return; + Log.i(TAG, "Client died: ending session: " + mSessionHandle); + try { + removeClientAndUnlinkToDeath(); + stopRanging(mSessionHandle); + closeRanging(mSessionHandle); + } catch (RemoteException e) { + Log.e(TAG, "Remote exception while handling client death", e); + } + } + } + + private void linkToVendorServiceDeath() { + IBinder binder = mVendorUwbAdapter.asBinder(); + try { + binder.linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(TAG, "Unable to link to vendor service death event.", e); + } + } + + @Override + public void binderDied() { + Log.i(TAG, "Vendor service died: sending session close callbacks"); + synchronized (mCallbacksMap) { + for (Map.Entry<SessionHandle, UwbRangingCallbacksWrapper> e : mCallbacksMap.entrySet()) { + try { + e.getValue().mExternalCb.onRangingClosed( + e.getKey(), RangingSession.Callback.REASON_UNKNOWN, + new PersistableBundle()); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to send session close callback " + e.getKey(), ex); + } + } + // Clear all sessions. + mCallbacksMap.clear(); + } + mVendorUwbAdapter = null; + } + + private synchronized IUwbAdapter getVendorUwbAdapter() throws IllegalStateException { + if (mVendorUwbAdapter != null) return mVendorUwbAdapter; + mVendorUwbAdapter = mUwbInjector.getVendorService(); + if (mVendorUwbAdapter == null) { + throw new IllegalStateException("No vendor service found!"); + } + Log.i(TAG, "Retrieved vendor service"); + linkToVendorServiceDeath(); + return mVendorUwbAdapter; + } + + UwbServiceImpl(@NonNull Context context, @NonNull UwbInjector uwbInjector) { + mContext = context; + mUwbInjector = uwbInjector; + } + + @Override + public void registerAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks) + throws RemoteException { + getVendorUwbAdapter().registerAdapterStateCallbacks(adapterStateCallbacks); + } + + @Override + public void unregisterAdapterStateCallbacks(IUwbAdapterStateCallbacks adapterStateCallbacks) + throws RemoteException { + getVendorUwbAdapter().unregisterAdapterStateCallbacks(adapterStateCallbacks); + } + + @Override + public long getTimestampResolutionNanos() throws RemoteException { + return getVendorUwbAdapter().getTimestampResolutionNanos(); + } + + @Override + public PersistableBundle getSpecificationInfo() throws RemoteException { + return getVendorUwbAdapter().getSpecificationInfo(); + } + + @Override + public void openRanging(SessionHandle sessionHandle, IUwbRangingCallbacks rangingCallbacks, + PersistableBundle parameters) throws RemoteException { + UwbRangingCallbacksWrapper wrapperCb = + new UwbRangingCallbacksWrapper(sessionHandle, rangingCallbacks); + synchronized (mCallbacksMap) { + mCallbacksMap.put(sessionHandle, wrapperCb); + } + getVendorUwbAdapter().openRanging(sessionHandle, wrapperCb, parameters); + } + + @Override + public void startRanging(SessionHandle sessionHandle, PersistableBundle parameters) + throws RemoteException { + // TODO: Perform permission checks. + getVendorUwbAdapter().startRanging(sessionHandle, parameters); + } + + @Override + public void reconfigureRanging(SessionHandle sessionHandle, PersistableBundle parameters) + throws RemoteException { + getVendorUwbAdapter().reconfigureRanging(sessionHandle, parameters); + } + + @Override + public void stopRanging(SessionHandle sessionHandle) throws RemoteException { + getVendorUwbAdapter().stopRanging(sessionHandle); + } + + @Override + public void closeRanging(SessionHandle sessionHandle) throws RemoteException { + getVendorUwbAdapter().closeRanging(sessionHandle); + } + +} diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index f6d18fcd9ab3..96e715ee0495 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -637,18 +637,18 @@ public final class AccessNetworkConstants { this.band = band; this.downlinkLowKhz = downlinkLowKhz; this.downlinkOffset = downlinkOffset; + this.downlinkRange = downlinkRange; this.uplinkLowKhz = uplinkLowKhz; this.uplinkOffset = uplinkOffset; - this.downlinkRange = downlinkRange; this.uplinkRange = uplinkRange; } int band; int downlinkLowKhz; int downlinkOffset; + int downlinkRange; int uplinkLowKhz; int uplinkOffset; - int downlinkRange; int uplinkRange; } diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index f29f3bd352be..6b820459be98 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -598,7 +598,8 @@ public class AccessNetworkUtils { : earfcnFrequency.downlinkOffset; break; } else { - Log.e(TAG, "Band and the range of EARFCN are not consistent."); + Rlog.w(TAG,"Band and the range of EARFCN are not consistent: band = " + band + + " ,earfcn = " + earfcn + " ,isUplink = " + isUplink); return INVALID_FREQUENCY; } } @@ -617,7 +618,7 @@ public class AccessNetworkUtils { } private static boolean isInEarfcnRange(int earfcn, EutranBandArfcnFrequency earfcnFrequency, - boolean isUplink) { + boolean isUplink) { if (isUplink) { return earfcn >= earfcnFrequency.uplinkOffset && earfcn <= earfcnFrequency.uplinkRange; } else { @@ -640,7 +641,8 @@ public class AccessNetworkUtils { : uarfcnFrequency.downlinkOffset; break; } else { - Log.e(TAG, "Band and the range of UARFCN are not consistent."); + Rlog.w(TAG,"Band and the range of UARFCN are not consistent: band = " + band + + " ,uarfcn = " + uarfcn + " ,isUplink = " + isUplink); return INVALID_FREQUENCY; } } @@ -716,7 +718,8 @@ public class AccessNetworkUtils { arfcnOffset); break; } else { - Log.e(TAG, "Band and the range of ARFCN are not consistent."); + Rlog.w(TAG,"Band and the range of ARFCN are not consistent: band = " + band + + " ,arfcn = " + arfcn + " ,isUplink = " + isUplink); return INVALID_FREQUENCY; } } @@ -733,7 +736,7 @@ public class AccessNetworkUtils { * Downlink actual frequency(kHz) = Uplink actual frequency + 10 */ private static int convertArfcnToFrequency(int arfcn, int uplinkFrequencyFirstKhz, - int arfcnOffset) { + int arfcnOffset) { return uplinkFrequencyFirstKhz + 200 * (arfcn - arfcnOffset); } diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java index d21fcab264d3..391372ac8ac1 100644 --- a/telephony/java/android/telephony/ims/SipMessage.java +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -203,23 +203,6 @@ public final class SipMessage implements Parcelable { } /** - * @return the UTF-8 encoded SIP message. - * @deprecated Use {@link #toEncodedMessage} instead - */ - @Deprecated - public @NonNull byte[] getEncodedMessage() { - byte[] header = new StringBuilder() - .append(mStartLine) - .append(mHeaderSection) - .append(CRLF) - .toString().getBytes(UTF_8); - byte[] sipMessage = new byte[header.length + mContent.length]; - System.arraycopy(header, 0, sipMessage, 0, header.length); - System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length); - return sipMessage; - } - - /** * According RFC-3261 section 7, SIP is a text protocol and uses the UTF-8 charset. Its format * consists of a start-line, one or more header fields, an empty line indicating the end of the * header fields, and an optional message-body. diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java index ec859955694c..2d230a74a477 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java @@ -23,6 +23,7 @@ import android.app.blob.BlobStoreManager; import android.app.blob.LeaseInfo; import android.content.Context; import android.content.res.Resources; +import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -56,6 +57,16 @@ public class Utils { } } + public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input) + throws IOException { + try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) { + try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( + session.openWrite(0, -1))) { + FileUtils.copy(in, out); + } + } + } + public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input, long lengthBytes) throws IOException { try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index ed0a98d3c1ec..059361525733 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -19,19 +19,21 @@ package com.android.server.wm.flicker import android.platform.helpers.IAppHelper import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_LAYER_NAME import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.STATUS_BAR_WINDOW_NAME -const val WALLPAPER_TITLE = "Wallpaper" +val LAUNCHER_TITLE = arrayOf("Wallpaper", "Launcher", "com.google.android.googlequicksearchbox") fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() { assertWm { - this.showsAboveAppWindow(NAV_BAR_LAYER_NAME) + this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME) } } fun FlickerTestParameter.navBarWindowIsAlwaysVisible() { assertWm { - this.showsAboveAppWindow(NAV_BAR_LAYER_NAME) + this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME) } } @@ -39,23 +41,23 @@ fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelpe assertWm { this.showsAppWindowOnTop(testApp.getPackage()) .then() - .showsAppWindowOnTop("Launcher") + .showsAppWindowOnTop(*LAUNCHER_TITLE) } } -fun FlickerTestParameter.wallpaperWindowBecomesVisible() { +fun FlickerTestParameter.launcherWindowBecomesVisible() { assertWm { - this.hidesBelowAppWindow(WALLPAPER_TITLE) + this.hidesBelowAppWindow(*LAUNCHER_TITLE) .then() - .showsBelowAppWindow(WALLPAPER_TITLE) + .showsBelowAppWindow(*LAUNCHER_TITLE) } } -fun FlickerTestParameter.wallpaperWindowBecomesInvisible() { +fun FlickerTestParameter.launcherWindowBecomesInvisible() { assertWm { - this.showsBelowAppWindow(WALLPAPER_TITLE) + this.showsBelowAppWindow(*LAUNCHER_TITLE) .then() - .hidesBelowAppWindow(WALLPAPER_TITLE) + .hidesBelowAppWindow(*LAUNCHER_TITLE) } } @@ -130,15 +132,15 @@ fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = fal fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) { if (rotatesScreen) { assertLayers { - this.isVisible(STATUS_BAR_WINDOW_NAME) + this.isVisible(STATUS_BAR_LAYER_NAME) .then() - .isInvisible(STATUS_BAR_WINDOW_NAME) + .isInvisible(STATUS_BAR_LAYER_NAME) .then() - .isVisible(STATUS_BAR_WINDOW_NAME) + .isVisible(STATUS_BAR_LAYER_NAME) } } else { assertLayers { - this.isVisible(STATUS_BAR_WINDOW_NAME) + this.isVisible(STATUS_BAR_LAYER_NAME) } } } @@ -168,34 +170,27 @@ fun FlickerTestParameter.statusBarLayerRotatesScales( val endingPos = WindowUtils.getStatusBarPosition(endRotation) assertLayersStart { - this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(startingPos) + this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(startingPos) } assertLayersEnd { - this.visibleRegion(STATUS_BAR_WINDOW_NAME).coversExactly(endingPos) + this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(endingPos) } } -fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) { +fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) { assertLayers { - this.isVisible(WALLPAPER_TITLE) + this.isVisible(*LAUNCHER_TITLE) .then() - .isInvisible(WALLPAPER_TITLE) .isVisible(appName) } } -fun FlickerTestParameter.wallpaperLayerReplacesAppLayer(testApp: IAppHelper) { +fun FlickerTestParameter.launcherLayerReplacesApp(testApp: IAppHelper) { assertLayers { this.isVisible(testApp.getPackage()) .then() .isInvisible(testApp.getPackage()) - .isVisible(WALLPAPER_TITLE) - } -} - -fun FlickerTestParameter.layerAlwaysVisible(packageName: String) { - assertLayers { - this.isVisible(packageName) + .isVisible(*LAUNCHER_TITLE) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index c92d40cdd555..db91eb21d532 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,12 +16,14 @@ package com.android.server.wm.flicker.close +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import org.junit.FixMethodOrder +import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -44,6 +46,30 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio } } + @FlakyTest(bugId = 185401242) + @Test + override fun launcherLayerReplacesApp() { + super.launcherLayerReplacesApp() + } + + @FlakyTest(bugId = 185401242) + @Test + override fun launcherReplacesAppWindowAsTopWindow() { + super.launcherReplacesAppWindowAsTopWindow() + } + + @FlakyTest(bugId = 185401242) + @Test + override fun launcherWindowBecomesVisible() { + super.launcherWindowBecomesVisible() + } + + @FlakyTest(bugId = 185401242) + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index a524466328f5..9ac504ba3e6f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -36,8 +36,8 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer -import com.android.server.wm.flicker.wallpaperWindowBecomesVisible +import com.android.server.wm.flicker.launcherLayerReplacesApp +import com.android.server.wm.flicker.launcherWindowBecomesVisible import org.junit.Test abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) { @@ -76,16 +76,16 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) testSpec.statusBarWindowIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test open fun navBarLayerIsAlwaysVisible() { - testSpec.navBarLayerIsAlwaysVisible() + testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated) } - @Presubmit + @FlakyTest @Test open fun statusBarLayerIsAlwaysVisible() { - testSpec.statusBarLayerIsAlwaysVisible() + testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated) } @FlakyTest @@ -130,13 +130,13 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) @Presubmit @Test - open fun wallpaperWindowBecomesVisible() { - testSpec.wallpaperWindowBecomesVisible() + open fun launcherWindowBecomesVisible() { + testSpec.launcherWindowBecomesVisible() } @Presubmit @Test - open fun wallpaperLayerReplacesAppLayer() { - testSpec.wallpaperLayerReplacesAppLayer(testApp) + open fun launcherLayerReplacesApp() { + testSpec.launcherLayerReplacesApp(testApp) } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt index 90c23385d16b..3bd19ea74f1d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -50,6 +51,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 185400889) class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation) @@ -64,7 +66,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter } } teardown { - test { + eachRun { testApp.exit(wmHelper) } } @@ -104,7 +106,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) @@ -141,7 +143,10 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5) + .getConfigNonRotationTests(repetitions = 5, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt index dfb229d54c99..3cb58b9b25d6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -65,7 +66,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete } } teardown { - test { + eachRun { testApp.exit(wmHelper) } } @@ -95,24 +96,24 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete } } - @Presubmit + @FlakyTest @Test fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() - @Presubmit + @FlakyTest @Test fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) - @Presubmit + @FlakyTest @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - @Presubmit + @FlakyTest @Test fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible() - @Presubmit + @FlakyTest @Test fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp) @@ -132,7 +133,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() @@ -150,7 +151,10 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete @JvmStatic fun getParams(): Collection<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = 5) + .getConfigNonRotationTests(repetitions = 1, + supportedNavigationModes = listOf( + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY) + ) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt index 6b8bf63aa926..cdec51d25d7f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt @@ -76,11 +76,11 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { } } - @Presubmit + @FlakyTest @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() @@ -106,7 +106,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { @Test fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @Presubmit + @FlakyTest @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation) @@ -138,7 +138,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) { testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation) } - @Presubmit + @FlakyTest @Test fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt index 9b37cafa74c3..22d341866088 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt @@ -20,6 +20,7 @@ import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface import android.view.WindowManagerPolicyConstants +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider @@ -97,11 +98,11 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) { } } - @Presubmit + @FlakyTest @Test fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible() - @Presubmit + @FlakyTest @Test fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt index 7ba9db19dd52..bb9cd6fef4a2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt @@ -34,7 +34,6 @@ import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.layerAlwaysVisible import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales @@ -113,7 +112,11 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`) + fun layerAlwaysVisible() { + testSpec.assertLayers { + this.isVisible(testApp.`package`) + } + } @Presubmit @Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt index be0357e826a8..55bbe3aa2b5c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt @@ -33,8 +33,8 @@ import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible -import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.launcherWindowBecomesInvisible +import com.android.server.wm.flicker.appLayerReplacesLauncher import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation @@ -107,7 +107,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() + fun launcherWindowBecomesInvisible() = testSpec.launcherWindowBecomesInvisible() @Presubmit @Test @@ -138,8 +138,8 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun appLayerReplacesWallpaperLayer() = - testSpec.appLayerReplacesWallpaperLayer(testAppComponentName.className) + fun appLayerReplacesLauncher() = + testSpec.appLayerReplacesLauncher(testAppComponentName.className) @FlakyTest @Test @@ -168,9 +168,9 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) { return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( repetitions = 1, + supportedRotations = listOf(Surface.ROTATION_0), supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY ) ) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt index 8e73ab18fc95..d0e95566f1b5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.ime import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.WindowManagerPolicyConstants +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -51,6 +52,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val testApp = SimpleAppHelper(instrumentation) @@ -131,7 +133,7 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame .getConfigNonRotationTests( repetitions = 3, supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY, + WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY, WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ) ) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt index 130860d31ac1..6a7309c4c120 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt @@ -18,10 +18,11 @@ package com.android.server.wm.flicker.launch import android.platform.helpers.IAppHelper import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.LAUNCHER_TITLE fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) { assertWm { - this.showsAppWindowOnTop("Launcher") + this.showsAppWindowOnTop(*LAUNCHER_TITLE) .then() .showsAppWindowOnTop("Snapshot", testApp.getPackage()) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 024983d68b21..559d95376b65 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -66,6 +66,18 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp super.visibleLayersShownMoreThanOneConsecutiveEntry() } + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() { + super.navBarLayerRotatesAndScales() + } + + @FlakyTest + @Test + override fun focusChanges() { + super.focusChanges() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 62b9b81ac35d..ad7ee3030ea8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -25,7 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible +import com.android.server.wm.flicker.launcherWindowBecomesInvisible import com.android.server.wm.flicker.dsl.FlickerBuilder import org.junit.FixMethodOrder import org.junit.Test @@ -70,8 +70,8 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio @Postsubmit @Test - override fun wallpaperWindowBecomesInvisible() { - testSpec.wallpaperWindowBecomesInvisible() + override fun launcherWindowBecomesInvisible() { + testSpec.launcherWindowBecomesInvisible() } @FlakyTest @@ -98,6 +98,12 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio super.focusChanges() } + @FlakyTest(bugId = 185400889) + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index cd5c61a5a927..26e77b6c4828 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -19,10 +19,11 @@ package com.android.server.wm.flicker.launch import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.appLayerReplacesLauncher import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.focusChanges @@ -39,7 +40,7 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible +import com.android.server.wm.flicker.launcherWindowBecomesInvisible import org.junit.Test abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { @@ -75,13 +76,13 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { testSpec.navBarWindowIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test open fun navBarLayerIsAlwaysVisible() { - testSpec.navBarLayerIsAlwaysVisible() + testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated) } - @Presubmit + @FlakyTest @Test open fun navBarLayerRotatesAndScales() { testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) @@ -93,10 +94,10 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { testSpec.statusBarWindowIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test open fun statusBarLayerIsAlwaysVisible() { - testSpec.statusBarLayerIsAlwaysVisible() + testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated) } @Presubmit @@ -134,13 +135,13 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) } - @Presubmit + @FlakyTest @Test - open fun appLayerReplacesWallpaperLayer() { - testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + open fun appLayerReplacesLauncher() { + testSpec.appLayerReplacesLauncher(testApp.`package`) } - @Presubmit + @FlakyTest @Test open fun appWindowReplacesLauncherAsTopWindow() { testSpec.appWindowReplacesLauncherAsTopWindow(testApp) @@ -148,7 +149,7 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { @Presubmit @Test - open fun wallpaperWindowBecomesInvisible() { - testSpec.wallpaperWindowBecomesInvisible() + open fun launcherWindowBecomesInvisible() { + testSpec.launcherWindowBecomesInvisible() } }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 38af8a7d24df..741aad714934 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.launch +import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -71,10 +72,28 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp @FlakyTest @Test + override fun navBarLayerRotatesAndScales() { + super.navBarLayerRotatesAndScales() + } + + @FlakyTest + @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() { super.visibleLayersShownMoreThanOneConsecutiveEntry() } + @Presubmit + @Test + override fun launcherWindowBecomesInvisible() { + super.launcherWindowBecomesInvisible() + } + + @FlakyTest + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 35ad59751639..5a8162efcb07 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.rotation -import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -58,7 +57,13 @@ class ChangeAppRotationTest( super.focusDoesNotChange() } - @Presubmit + @FlakyTest + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + + @FlakyTest @Test fun screenshotLayerBecomesInvisible() { testSpec.assertLayers { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index 298903570e76..ab8ebd923fd3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -36,6 +36,7 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Test abstract class RotationTransition(protected val testSpec: FlickerTestParameter) { @@ -68,16 +69,16 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) } } - @Presubmit + @FlakyTest @Test open fun navBarWindowIsAlwaysVisible() { testSpec.navBarWindowIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test open fun navBarLayerIsAlwaysVisible() { - testSpec.navBarLayerIsAlwaysVisible() + testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = true) } @FlakyTest @@ -87,16 +88,16 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) testSpec.config.startRotation, testSpec.config.endRotation) } - @Presubmit + @FlakyTest @Test open fun statusBarWindowIsAlwaysVisible() { testSpec.statusBarWindowIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test open fun statusBarLayerIsAlwaysVisible() { - testSpec.statusBarLayerIsAlwaysVisible() + testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true) } @FlakyTest @@ -110,7 +111,12 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter) @Test open fun visibleLayersShownMoreThanOneConsecutiveEntry() { testSpec.assertLayers { - this.visibleLayersShownMoreThanOneConsecutiveEntry() + this.visibleLayersShownMoreThanOneConsecutiveEntry( + ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME, + WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, + "SecondaryHomeHandle" + ) + ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index fe444bdecba7..a353c5962582 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.rotation -import android.platform.test.annotations.Presubmit import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory @@ -24,7 +23,6 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper -import com.android.server.wm.flicker.layerAlwaysVisible import com.android.server.wm.flicker.testapp.ActivityOptions import org.junit.FixMethodOrder import org.junit.Test @@ -60,27 +58,37 @@ class SeamlessAppRotationTest( @FlakyTest(bugId = 140855415) @Test - override fun navBarLayerRotatesAndScales() { - super.navBarLayerRotatesAndScales() + override fun statusBarWindowIsAlwaysVisible() { + super.statusBarWindowIsAlwaysVisible() } @FlakyTest(bugId = 140855415) @Test - override fun statusBarLayerRotatesScales() { - super.statusBarLayerRotatesScales() + override fun statusBarLayerIsAlwaysVisible() { + super.statusBarLayerIsAlwaysVisible() } - @Presubmit + @FlakyTest(bugId = 185400889) + @Test + override fun noUncoveredRegions() { + super.noUncoveredRegions() + } + + @FlakyTest(bugId = 185400889) @Test fun appLayerAlwaysVisible() { - testSpec.layerAlwaysVisible(testApp.`package`) + testSpec.assertLayers { + isVisible(testApp.`package`) + } } - @Presubmit + @FlakyTest(bugId = 185400889) @Test fun appLayerRotates() { testSpec.assertLayers { this.coversExactly(startingPos, testApp.`package`) + .then() + .coversExactly(endingPos, testApp.`package`) } } diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml index d573e93e4a4c..4f116698ba72 100644 --- a/tests/UpdatableSystemFontTest/AndroidTest.xml +++ b/tests/UpdatableSystemFontTest/AndroidTest.xml @@ -19,6 +19,11 @@ <!-- This test requires root to side load fs-verity cert. --> <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="EmojiRenderingTestApp.apk" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" /> diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/Android.bp b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/Android.bp new file mode 100644 index 000000000000..ed34fa9fc1d0 --- /dev/null +++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/Android.bp @@ -0,0 +1,32 @@ +// Copyright (C) 2021 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "EmojiRenderingTestApp", + manifest: "AndroidManifest.xml", + srcs: ["src/**/*.java"], + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..5d8f5fc2da93 --- /dev/null +++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2021 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" + package="com.android.emojirenderingtestapp"> + <application> + <activity android:name=".EmojiRenderingTestActivity"/> + </application> +</manifest> diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java new file mode 100644 index 000000000000..947e9c2ff56a --- /dev/null +++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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.emojirenderingtestapp; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** Test app to render an emoji. */ +public class EmojiRenderingTestActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LinearLayout container = new LinearLayout(this); + container.setOrientation(LinearLayout.VERTICAL); + TextView textView = new TextView(this); + textView.setText("\uD83E\uDD72"); // 🥲 + container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + setContentView(container); + } +} diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java index e68455612c7e..032da3f0af75 100644 --- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java +++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java @@ -36,7 +36,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -47,6 +46,9 @@ import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) public class UpdatableSystemFontTest extends BaseHostJUnit4Test { + private static final String SYSTEM_FONTS_DIR = "/system/fonts/"; + private static final String DATA_FONTS_DIR = "/data/fonts/files/"; + private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der"; private static final Pattern PATTERN_FONT = Pattern.compile("path = ([^, \n]*)"); @@ -72,6 +74,14 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG = "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig"; + private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp"; + private static final String EMOJI_RENDERING_TEST_ACTIVITY = + EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity"; + + private interface ThrowingSupplier<T> { + T get() throws Exception; + } + @Rule public final AddFsVerityCertRule mAddFsverityCertRule = new AddFsVerityCertRule(this, CERT_PATH); @@ -91,7 +101,10 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { expectRemoteCommandToSucceed(String.format("cmd font update %s %s", TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG)); String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); - assertThat(fontPath).startsWith("/data/fonts/files/"); + assertThat(fontPath).startsWith(DATA_FONTS_DIR); + // The updated font should be readable and unmodifiable. + expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null"); + expectRemoteCommandToFail("echo -n '' >> " + fontPath); } @Test @@ -102,8 +115,12 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { expectRemoteCommandToSucceed(String.format("cmd font update %s %s", TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG)); String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_TTF); - assertThat(fontPath2).startsWith("/data/fonts/files/"); + assertThat(fontPath2).startsWith(DATA_FONTS_DIR); assertThat(fontPath2).isNotEqualTo(fontPath); + // The new file should be readable. + expectRemoteCommandToSucceed("cat " + fontPath2 + " > /dev/null"); + // The old file should be still readable. + expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null"); } @Test @@ -119,25 +136,14 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { expectRemoteCommandToSucceed(String.format("cmd font update %s %s", TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG)); String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_TTF); - assertThat(fontPath).startsWith("/data/fonts/files/"); + assertThat(fontPath).startsWith(DATA_FONTS_DIR); assertThat(fontPath2).isNotEqualTo(fontPath); - assertThat(fontPath2).startsWith("/data/fonts/files/"); - assertThat(fontPath3).startsWith("/data/fonts/files/"); + assertThat(fontPath2).startsWith(DATA_FONTS_DIR); + assertThat(fontPath3).startsWith(DATA_FONTS_DIR); assertThat(fontPath3).isNotEqualTo(fontPath); } @Test - public void updatedFont_dataFileIsImmutableAndReadable() throws Exception { - expectRemoteCommandToSucceed(String.format("cmd font update %s %s", - TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG)); - String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); - assertThat(fontPath).startsWith("/data"); - - expectRemoteCommandToFail("echo -n '' >> " + fontPath); - expectRemoteCommandToSucceed("cat " + fontPath + " > /dev/null"); - } - - @Test public void updateFont_invalidCert() throws Exception { expectRemoteCommandToFail(String.format("cmd font update %s %s", TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG)); @@ -158,11 +164,37 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { } @Test + public void launchApp() throws Exception { + String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR); + expectRemoteCommandToSucceed("am force-stop " + EMOJI_RENDERING_TEST_APP_ID); + expectRemoteCommandToSucceed("am start-activity -n " + EMOJI_RENDERING_TEST_ACTIVITY); + waitUntil(TimeUnit.SECONDS.toMillis(5), () -> + isFileOpenedBy(fontPath, EMOJI_RENDERING_TEST_APP_ID)); + } + + @Test + public void launchApp_afterUpdateFont() throws Exception { + String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR); + expectRemoteCommandToSucceed(String.format("cmd font update %s %s", + TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG)); + String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); + assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR); + expectRemoteCommandToSucceed("am force-stop " + EMOJI_RENDERING_TEST_APP_ID); + expectRemoteCommandToSucceed("am start-activity -n " + EMOJI_RENDERING_TEST_ACTIVITY); + // The original font should NOT be opened by the app. + waitUntil(TimeUnit.SECONDS.toMillis(5), () -> + isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID) + && !isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID)); + } + + @Test public void reboot() throws Exception { expectRemoteCommandToSucceed(String.format("cmd font update %s %s", TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG)); String fontPath = getFontPath(NOTO_COLOR_EMOJI_TTF); - assertThat(fontPath).startsWith("/data/fonts/files/"); + assertThat(fontPath).startsWith(DATA_FONTS_DIR); expectRemoteCommandToSucceed("stop"); expectRemoteCommandToSucceed("start"); @@ -210,16 +242,40 @@ public class UpdatableSystemFontTest extends BaseHostJUnit4Test { }); } - private void waitUntil(long timeoutMillis, Supplier<Boolean> func) { + private void waitUntil(long timeoutMillis, ThrowingSupplier<Boolean> func) { long untilMillis = System.currentTimeMillis() + timeoutMillis; do { - if (func.get()) return; try { + if (func.get()) return; Thread.sleep(100); } catch (InterruptedException e) { throw new AssertionError("Interrupted", e); + } catch (Exception e) { + throw new AssertionError("Unexpected exception", e); } } while (System.currentTimeMillis() < untilMillis); throw new AssertionError("Timed out"); } + + private boolean isFileOpenedBy(String path, String appId) throws DeviceNotAvailableException { + String pid = pidOf(appId); + if (pid.isEmpty()) { + return false; + } + CommandResult result = getDevice().executeShellV2Command( + String.format("lsof -t -p %s '%s'", pid, path)); + if (result.getStatus() != CommandStatus.SUCCESS) { + return false; + } + // The file is open if the output of lsof is non-empty. + return !result.getStdout().trim().isEmpty(); + } + + private String pidOf(String appId) throws DeviceNotAvailableException { + CommandResult result = getDevice().executeShellV2Command("pidof " + appId); + if (result.getStatus() != CommandStatus.SUCCESS) { + return ""; + } + return result.getStdout().trim(); + } } diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp index 56f9df78c83e..39c424e31f0e 100644 --- a/tests/net/integration/Android.bp +++ b/tests/net/integration/Android.bp @@ -25,6 +25,7 @@ package { android_test { name: "FrameworksNetIntegrationTests", + defaults: ["framework-connectivity-test-defaults"], platform_apis: true, certificate: "platform", srcs: [ @@ -61,6 +62,7 @@ android_test { // Utilities for testing framework code both in integration and unit tests. java_library { name: "frameworks-net-integration-testutils", + defaults: ["framework-connectivity-test-defaults"], srcs: ["util/**/*.java", "util/**/*.kt"], static_libs: [ "androidx.annotation_annotation", diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 47e4b5e4942a..1b0a500c6ac6 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -9768,7 +9768,8 @@ public class ConnectivityServiceTest { return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(), nc, new NetworkScore.Builder().setLegacyInt(0).build(), mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, - INVALID_UID, mQosCallbackTracker, new ConnectivityService.Dependencies()); + INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker, + new ConnectivityService.Dependencies()); } @Test diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java index 116d755e30a4..36e229d8aa73 100644 --- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java @@ -71,6 +71,8 @@ public class LingerMonitorTest { static final int LOW_DAILY_LIMIT = 2; static final int HIGH_DAILY_LIMIT = 1000; + private static final int TEST_LINGER_DELAY_MS = 400; + LingerMonitor mMonitor; @Mock ConnectivityService mConnService; @@ -366,7 +368,7 @@ public class LingerMonitorTest { NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd, - mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), + mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS, mQosCallbackTracker, new ConnectivityService.Dependencies()); nai.everValidated = true; return nai; diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index a2223e8c1e9a..95a972652bf4 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -89,7 +89,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection mNetworkAgent = mock(VcnNetworkAgent.class); doReturn(mNetworkAgent) .when(mDeps) - .newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any()); + .newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any()); mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); @@ -216,7 +216,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection any(), any(), any(), - anyInt(), + any(), any(), any(), any(), @@ -244,7 +244,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection any(String.class), ncCaptor.capture(), lpCaptor.capture(), - anyInt(), + any(), argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE), any(), any(), @@ -297,7 +297,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection startingInternalAddrs.equals(lp.getLinkAddresses()) && Collections.singletonList(TEST_DNS_ADDR) .equals(lp.getDnsServers())), - anyInt(), + any(), any(), any(), any(), @@ -356,7 +356,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection any(), any(), any(), - anyInt(), + any(), any(), any(), unwantedCallbackCaptor.capture(), diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java index 5f27fabb62b0..ac0edaa3b579 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -65,6 +65,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mGatewayStatusCallback, + true /* isMobileDataEnabled */, mDeps); vgc.setIsQuitting(true); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index ced8745e89c9..9705f0fc6bbc 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -16,6 +16,8 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; @@ -89,7 +91,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { doReturn(mWifiInfo).when(mWifiInfo).makeCopy(anyLong()); } - private void verifyBuildNetworkCapabilitiesCommon(int transportType) { + private void verifyBuildNetworkCapabilitiesCommon( + int transportType, boolean isMobileDataEnabled) { final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder(); capBuilder.addTransportType(transportType); capBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); @@ -109,10 +112,22 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { capBuilder.build(), new LinkProperties(), false); final NetworkCapabilities vcnCaps = VcnGatewayConnection.buildNetworkCapabilities( - VcnGatewayConnectionConfigTest.buildTestConfig(), record); + VcnGatewayConnectionConfigTest.buildTestConfig(), + record, + isMobileDataEnabled); + assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR)); assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + + for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) { + if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) { + assertEquals(isMobileDataEnabled, vcnCaps.hasCapability(cap)); + } else { + assertTrue(vcnCaps.hasCapability(cap)); + } + } + assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids()); assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo); @@ -126,12 +141,17 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { @Test public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception { - verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI); + verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI, true /* isMobileDataEnabled */); } @Test public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception { - verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR); + verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, true /* isMobileDataEnabled */); + } + + @Test + public void testBuildNetworkCapabilitiesMobileDataDisabled() throws Exception { + verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, false /* isMobileDataEnabled */); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 98d553dab01a..284f1f88658e 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -202,6 +202,7 @@ public class VcnGatewayConnectionTestBase { TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mGatewayStatusCallback, + true /* isMobileDataEnabled */, mDeps); } diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java index f943f34c9d52..72db55b3f4c5 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java @@ -16,12 +16,18 @@ package com.android.server.vcn; +import static android.net.NetworkProvider.NetworkOfferCallback; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import android.annotation.NonNull; import android.content.Context; +import android.net.ConnectivityManager; import android.net.NetworkRequest; import android.os.test.TestLooper; @@ -33,18 +39,23 @@ import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.util.ArrayList; +import java.util.List; /** Tests for TelephonySubscriptionTracker */ @RunWith(AndroidJUnit4.class) @SmallTest public class VcnNetworkProviderTest { private static final int TEST_SCORE_UNSATISFIED = 0; - private static final int TEST_SCORE_HIGH = 100; private static final int TEST_PROVIDER_ID = 1; @NonNull private final Context mContext; @NonNull private final TestLooper mTestLooper; + @NonNull private VcnNetworkProvider.Dependencies mDeps; + @NonNull private ConnectivityManager mConnMgr; @NonNull private VcnNetworkProvider mVcnNetworkProvider; @NonNull private NetworkRequestListener mListener; @@ -55,27 +66,48 @@ public class VcnNetworkProviderTest { @Before public void setUp() throws Exception { - mVcnNetworkProvider = new VcnNetworkProvider(mContext, mTestLooper.getLooper()); + mDeps = mock(VcnNetworkProvider.Dependencies.class); + mConnMgr = mock(ConnectivityManager.class); + VcnTestUtils.setupSystemService( + mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); + + mVcnNetworkProvider = new VcnNetworkProvider(mContext, mTestLooper.getLooper(), mDeps); mListener = mock(NetworkRequestListener.class); } - @Test - public void testRequestsPassedToRegisteredListeners() throws Exception { - mVcnNetworkProvider.registerListener(mListener); + private NetworkOfferCallback verifyRegisterAndGetOfferCallback() throws Exception { + mVcnNetworkProvider.register(); + + final ArgumentCaptor<NetworkOfferCallback> cbCaptor = + ArgumentCaptor.forClass(NetworkOfferCallback.class); + + verify(mConnMgr).registerNetworkProvider(eq(mVcnNetworkProvider)); + verify(mDeps) + .registerNetworkOffer( + eq(mVcnNetworkProvider), + argThat( + score -> + score.getLegacyInt() + == Vcn.getNetworkScore().getLegacyInt()), + any(), + any(), + cbCaptor.capture()); + + return cbCaptor.getValue(); + } - final NetworkRequest request = mock(NetworkRequest.class); - mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID); - verify(mListener).onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID); + @Test + public void testRegister() throws Exception { + verifyRegisterAndGetOfferCallback(); } @Test - public void testRequestsPassedToRegisteredListeners_satisfiedByHighScoringProvider() - throws Exception { + public void testRequestsPassedToRegisteredListeners() throws Exception { mVcnNetworkProvider.registerListener(mListener); final NetworkRequest request = mock(NetworkRequest.class); - mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_HIGH, TEST_PROVIDER_ID); - verify(mListener).onNetworkRequested(request, TEST_SCORE_HIGH, TEST_PROVIDER_ID); + verifyRegisterAndGetOfferCallback().onNetworkNeeded(request); + verify(mListener).onNetworkRequested(request); } @Test @@ -84,7 +116,33 @@ public class VcnNetworkProviderTest { mVcnNetworkProvider.unregisterListener(mListener); final NetworkRequest request = mock(NetworkRequest.class); - mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID); + verifyRegisterAndGetOfferCallback().onNetworkNeeded(request); + verifyNoMoreInteractions(mListener); + } + + @Test + public void testCachedRequestsPassedOnRegister() throws Exception { + final List<NetworkRequest> requests = new ArrayList<>(); + final NetworkOfferCallback offerCb = verifyRegisterAndGetOfferCallback(); + + for (int i = 0; i < 10; i++) { + // Build unique network requests; in this case, iterate down the capabilities as a way + // to unique-ify requests. + final NetworkRequest request = + new NetworkRequest.Builder().clearCapabilities().addCapability(i).build(); + + requests.add(request); + offerCb.onNetworkNeeded(request); + } + + // Remove one, and verify that it is never sent to the listeners. + final NetworkRequest removed = requests.remove(0); + offerCb.onNetworkUnneeded(removed); + + mVcnNetworkProvider.registerListener(mListener); + for (NetworkRequest request : requests) { + verify(mListener).onNetworkRequested(request); + } verifyNoMoreInteractions(mListener); } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index 90eb75e865e7..736fabdb1ac5 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -16,16 +16,24 @@ package com.android.server.vcn; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; +import static com.android.server.vcn.Vcn.VcnContentResolver; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; @@ -35,12 +43,16 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; +import android.database.ContentObserver; import android.net.NetworkRequest; +import android.net.Uri; import android.net.vcn.VcnConfig; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; import android.os.ParcelUuid; import android.os.test.TestLooper; +import android.provider.Settings; +import android.telephony.TelephonyManager; import android.util.ArraySet; import com.android.server.VcnManagementService.VcnCallback; @@ -53,23 +65,31 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; public class VcnTest { private static final String PKG_NAME = VcnTest.class.getPackage().getName(); private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0)); - private static final int NETWORK_SCORE = 0; - private static final int PROVIDER_ID = 5; + private static final boolean MOBILE_DATA_ENABLED = true; + private static final Set<Integer> TEST_SUB_IDS_IN_GROUP = + new ArraySet<>(Arrays.asList(1, 2, 3)); private static final int[][] TEST_CAPS = new int[][] { - new int[] {NET_CAPABILITY_MMS, NET_CAPABILITY_INTERNET}, - new int[] {NET_CAPABILITY_DUN} + new int[] {NET_CAPABILITY_IMS, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN}, + new int[] {NET_CAPABILITY_CBS, NET_CAPABILITY_INTERNET}, + new int[] {NET_CAPABILITY_FOTA, NET_CAPABILITY_DUN}, + new int[] {NET_CAPABILITY_MMS} }; private Context mContext; private VcnContext mVcnContext; + private TelephonyManager mTelephonyManager; + private VcnContentResolver mContentResolver; private TelephonySubscriptionSnapshot mSubscriptionSnapshot; private VcnNetworkProvider mVcnNetworkProvider; private VcnCallback mVcnCallback; @@ -86,6 +106,9 @@ public class VcnTest { public void setUp() { mContext = mock(Context.class); mVcnContext = mock(VcnContext.class); + mTelephonyManager = + setupAndGetTelephonyManager(MOBILE_DATA_ENABLED /* isMobileDataEnabled */); + mContentResolver = mock(VcnContentResolver.class); mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class); mVcnNetworkProvider = mock(VcnNetworkProvider.class); mVcnCallback = mock(VcnCallback.class); @@ -97,12 +120,15 @@ public class VcnTest { doReturn(mContext).when(mVcnContext).getContext(); doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); + doReturn(mContentResolver).when(mDeps).newVcnContentResolver(eq(mVcnContext)); // Setup VcnGatewayConnection instance generation doAnswer((invocation) -> { // Mock-within a doAnswer is safe, because it doesn't actually run nested. return mock(VcnGatewayConnection.class); - }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any()); + }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any(), anyBoolean()); + + doReturn(TEST_SUB_IDS_IN_GROUP).when(mSubscriptionSnapshot).getAllSubIdsInGroup(any()); mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class); @@ -123,6 +149,16 @@ public class VcnTest { mDeps); } + private TelephonyManager setupAndGetTelephonyManager(boolean isMobileDataEnabled) { + final TelephonyManager telephonyManager = mock(TelephonyManager.class); + VcnTestUtils.setupSystemService( + mContext, telephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class); + doReturn(telephonyManager).when(telephonyManager).createForSubscriptionId(anyInt()); + doReturn(isMobileDataEnabled).when(telephonyManager).isDataEnabled(); + + return telephonyManager; + } + private NetworkRequestListener verifyAndGetRequestListener() { ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor = ArgumentCaptor.forClass(NetworkRequestListener.class); @@ -139,7 +175,7 @@ public class VcnTest { requestBuilder.addCapability(netCapability); } - requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID); + requestListener.onNetworkRequested(requestBuilder.build()); mTestLooper.dispatchAll(); } @@ -164,6 +200,39 @@ public class VcnTest { } @Test + public void testContentObserverRegistered() { + // Validate state from setUp() + final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); + verify(mContentResolver) + .registerContentObserver(eq(uri), eq(true), any(ContentObserver.class)); + } + + @Test + public void testMobileDataStateCheckedOnInitialization_enabled() { + // Validate state from setUp() + assertTrue(mVcn.isMobileDataEnabled()); + verify(mTelephonyManager).isDataEnabled(); + } + + @Test + public void testMobileDataStateCheckedOnInitialization_disabled() { + // Build and setup new telephonyManager to ensure method call count is reset. + final TelephonyManager telephonyManager = + setupAndGetTelephonyManager(false /* isMobileDataEnabled */); + final Vcn vcn = + new Vcn( + mVcnContext, + TEST_SUB_GROUP, + mConfig, + mSubscriptionSnapshot, + mVcnCallback, + mDeps); + + assertFalse(vcn.isMobileDataEnabled()); + verify(mTelephonyManager).isDataEnabled(); + } + + @Test public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() { verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_ACTIVE); } @@ -193,7 +262,8 @@ public class VcnTest { eq(TEST_SUB_GROUP), eq(mSubscriptionSnapshot), any(), - mGatewayStatusCallbackCaptor.capture()); + mGatewayStatusCallbackCaptor.capture(), + eq(MOBILE_DATA_ENABLED)); return gatewayConnections; } @@ -256,20 +326,21 @@ public class VcnTest { mTestLooper.dispatchAll(); // Verify that the VCN requests the networkRequests be resent - assertEquals(1, mVcn.getVcnGatewayConnections().size()); + assertEquals(gatewayConnections.size() - 1, mVcn.getVcnGatewayConnections().size()); verify(mVcnNetworkProvider).resendAllRequests(requestListener); // Verify that the VcnGatewayConnection is restarted if a request exists for it triggerVcnRequestListeners(requestListener); mTestLooper.dispatchAll(); - assertEquals(2, mVcn.getVcnGatewayConnections().size()); + assertEquals(gatewayConnections.size(), mVcn.getVcnGatewayConnections().size()); verify(mDeps, times(gatewayConnections.size() + 1)) .newVcnGatewayConnection( eq(mVcnContext), eq(TEST_SUB_GROUP), eq(mSubscriptionSnapshot), any(), - mGatewayStatusCallbackCaptor.capture()); + mGatewayStatusCallbackCaptor.capture(), + anyBoolean()); } @Test @@ -286,7 +357,7 @@ public class VcnTest { public void testUpdateConfigReevaluatesGatewayConnections() { final NetworkRequestListener requestListener = verifyAndGetRequestListener(); startGatewaysAndGetGatewayConnections(requestListener); - assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size()); + assertEquals(TEST_CAPS.length, mVcn.getVcnGatewayConnectionConfigMap().size()); // Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn // down. Reuse existing VcnGatewayConnectionConfig so that the gateway connection name @@ -309,4 +380,57 @@ public class VcnTest { verify(removedGatewayConnection).teardownAsynchronously(); verify(mVcnNetworkProvider).resendAllRequests(requestListener); } + + private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) { + final ArgumentCaptor<ContentObserver> captor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture()); + final ContentObserver contentObserver = captor.getValue(); + + // Start VcnGatewayConnections + mVcn.setMobileDataEnabled(startingToggleState); + triggerVcnRequestListeners(verifyAndGetRequestListener()); + final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways = + mVcn.getVcnGatewayConnectionConfigMap(); + + // Trigger data toggle change. + doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled(); + contentObserver.onChange(false /* selfChange, ignored */); + mTestLooper.dispatchAll(); + + // Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the + // toggle state changed. + for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) { + final Set<Integer> exposedCaps = entry.getKey().getAllExposedCapabilities(); + if (startingToggleState != endingToggleState + && (exposedCaps.contains(NET_CAPABILITY_INTERNET) + || exposedCaps.contains(NET_CAPABILITY_DUN))) { + verify(entry.getValue()).teardownAsynchronously(); + } else { + verify(entry.getValue(), never()).teardownAsynchronously(); + } + } + + assertEquals(endingToggleState, mVcn.isMobileDataEnabled()); + } + + @Test + public void testMobileDataEnabled() { + verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */); + } + + @Test + public void testMobileDataDisabled() { + verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */); + } + + @Test + public void testMobileDataObserverFiredWithoutChanges_dataEnabled() { + verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */); + } + + @Test + public void testMobileDataObserverFiredWithoutChanges_dataDisabled() { + verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */); + } } diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh index 451fed6be353..72fb86737488 100755 --- a/tools/hiddenapi/checksorted_sha.sh +++ b/tools/hiddenapi/checksorted_sha.sh @@ -1,10 +1,10 @@ #!/bin/bash set -e LOCAL_DIR="$( dirname ${BASH_SOURCE} )" -git show --name-only --pretty=format: $1 | grep "boot/hiddenapi/hiddenapi-.*txt" | while read file; do +git show --name-only --pretty=format: $1 | grep "hiddenapi/hiddenapi-.*txt" | while read file; do diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh ) || { echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m" - echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $2/frameworks/base/$file\e[0m" + echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $PWD/$file\e[0m" exit 1 } done diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh index 822aba4ee43b..8b18f9b1920e 100755 --- a/tools/hiddenapi/exclude.sh +++ b/tools/hiddenapi/exclude.sh @@ -10,7 +10,6 @@ LIBCORE_PACKAGES="\ android.system \ android.test \ com.android.bouncycastle \ - com.android.i18n.phonenumbers \ com.android.okhttp \ com.sun \ dalvik \ |