diff options
224 files changed, 4898 insertions, 2197 deletions
diff --git a/Android.bp b/Android.bp index b0e0b35a1f76..7f4cd849ab77 100644 --- a/Android.bp +++ b/Android.bp @@ -722,6 +722,7 @@ filegroup { srcs: [ "core/java/android/annotation/StringDef.java", "core/java/android/net/annotations/PolicyDirection.java", + "core/java/com/android/internal/util/HexDump.java", "core/java/com/android/internal/util/IState.java", "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", diff --git a/Android.mk b/Android.mk index aea0c951052f..3b307146770e 100644 --- a/Android.mk +++ b/Android.mk @@ -39,11 +39,6 @@ INTERNAL_SDK_SOURCE_DIRS := $(addprefix $(LOCAL_PATH)/,$(dirs_to_document)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE)) $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE)) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_MODULE_LIB_API_FILE):apistubs/android/module-lib/api/android.txt) -$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_SERVER_API_FILE):apistubs/android/system-server/api/android.txt) # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip. # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to diff --git a/StubLibraries.bp b/StubLibraries.bp index 12f211df4549..ccd873352a33 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -115,6 +115,11 @@ droidstubs { baseline_file: "api/lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/public/api", + dest: "android.txt", + }, jdiff_enabled: true, } @@ -156,6 +161,11 @@ droidstubs { baseline_file: "api/system-lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system/api", + dest: "android.txt", + }, jdiff_enabled: true, } @@ -179,6 +189,11 @@ droidstubs { baseline_file: "api/test-lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/test/api", + dest: "android.txt", + }, } ///////////////////////////////////////////////////////////////////// @@ -214,6 +229,11 @@ droidstubs { baseline_file: "api/module-lib-lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/module-lib/api", + dest: "android.txt", + }, } 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 e5a685f15df8..c8ca44b6ef74 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -116,7 +116,7 @@ class BlobMetadata { return mUserId; } - void addCommitter(@NonNull Committer committer) { + void addOrReplaceCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing // committer before adding the new one. @@ -139,6 +139,12 @@ class BlobMetadata { } } + void removeCommitter(@NonNull Committer committer) { + synchronized (mMetadataLock) { + mCommitters.remove(committer); + } + } + void removeInvalidCommitters(SparseArray<String> packages) { synchronized (mMetadataLock) { mCommitters.removeIf(committer -> @@ -154,7 +160,7 @@ class BlobMetadata { } } - void addLeasee(String callingPackage, int callingUid, int descriptionResId, + void addOrReplaceLeasee(String callingPackage, int callingUid, int descriptionResId, CharSequence description, long leaseExpiryTimeMillis) { synchronized (mMetadataLock) { // We need to override the leasee data, so first remove any existing 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 65ccb997343b..e472d052f32f 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -401,7 +401,7 @@ public class BlobStoreManagerService extends SystemService { throw new LimitExceededException("Total amount of data with an active lease" + " is exceeding the max limit"); } - blobMetadata.addLeasee(callingPackage, callingUid, + blobMetadata.addOrReplaceLeasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis); if (LOGV) { Slog.v(TAG, "Acquired lease on " + blobHandle @@ -573,12 +573,16 @@ public class BlobStoreManagerService extends SystemService { final Committer newCommitter = new Committer(session.getOwnerPackageName(), session.getOwnerUid(), session.getBlobAccessMode()); final Committer existingCommitter = blob.getExistingCommitter(newCommitter); - blob.addCommitter(newCommitter); + blob.addOrReplaceCommitter(newCommitter); try { writeBlobsInfoLocked(); session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS); } catch (Exception e) { - blob.addCommitter(existingCommitter); + if (existingCommitter == null) { + blob.removeCommitter(newCommitter); + } else { + blob.addOrReplaceCommitter(existingCommitter); + } session.sendCommitCallbackResult(COMMIT_RESULT_ERROR); } getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())) @@ -1349,8 +1353,15 @@ public class BlobStoreManagerService extends SystemService { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Caller is not allowed to call this; caller=" + Binder.getCallingUid()); - mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null) - .recycleOnUse()); + // We post messages back and forth between mHandler thread and mBackgroundHandler + // thread while committing a blob. We need to replicate the same pattern here to + // ensure pending messages have been handled. + mHandler.post(() -> { + mBackgroundHandler.post(() -> { + mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null) + .recycleOnUse()); + }); + }); } @Override diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp index 6d962008460f..3fefeb51aa11 100644 --- a/apex/permission/framework/Android.bp +++ b/apex/permission/framework/Android.bp @@ -84,20 +84,17 @@ droidstubs { java_library { name: "framework-permission-stubs-publicapi", srcs: [ ":framework-permission-stubs-srcs-publicapi" ], - sdk_version: "system_current", - installable: false, + defaults: ["framework-module-stubs-lib-defaults-publicapi"], } java_library { name: "framework-permission-stubs-systemapi", srcs: [ ":framework-permission-stubs-srcs-systemapi" ], - sdk_version: "system_current", - installable: false, + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-permission-stubs-module_libs_api", srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ], - sdk_version: "system_current", - installable: false, + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], } diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 8185bb036b22..804eb030e795 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -46,19 +46,11 @@ filegroup { "//frameworks/base/apex/statsd:__subpackages__", ], } - -java_defaults { - name: "framework-statsd-defaults", - sdk_version: "module_current", - libs: [ "framework-annotations-lib" ], -} - java_library { name: "framework-statsd", - defaults: [ - "framework-statsd-defaults", - ], installable: true, + sdk_version: "module_current", + libs: [ "framework-annotations-lib" ], srcs: [ ":framework-statsd-sources", @@ -129,39 +121,33 @@ droidstubs { java_library { name: "framework-statsd-stubs-publicapi", - defaults: [ - "framework-statsd-defaults", - ], + defaults: ["framework-module-stubs-lib-defaults-publicapi"], srcs: [ ":framework-statsd-stubs-srcs-publicapi" ], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex - ] + ], } java_library { name: "framework-statsd-stubs-systemapi", - defaults: [ - "framework-statsd-defaults", - ], + defaults: ["framework-module-stubs-lib-defaults-systemapi"], srcs: [ ":framework-statsd-stubs-srcs-systemapi" ], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex - ] + ], } java_library { name: "framework-statsd-stubs-module_libs_api", - defaults: [ - "framework-statsd-defaults", - ], + defaults: ["framework-module-stubs-lib-defaults-module_libs_api"], srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ], visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex "//frameworks/opt/net/wifi/service" // wifi service - ] + ], } android_test { diff --git a/api/current.txt b/api/current.txt index eb16b5e560c7..aca06fe4d3a6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9,7 +9,6 @@ package android { ctor public Manifest.permission(); field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER"; field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"; - field public static final String ACCESS_CALL_AUDIO = "android.permission.ACCESS_CALL_AUDIO"; field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES"; field public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"; field public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"; @@ -278,7 +277,7 @@ package android { field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8 field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9 - field public static final int actor = 16844312; // 0x1010618 + field public static final int actor = 16844313; // 0x1010619 field public static final int addPrintersActivity = 16843750; // 0x10103e6 field public static final int addStatesFromChildren = 16842992; // 0x10100f0 field public static final int adjustViewBounds = 16843038; // 0x101011e @@ -328,7 +327,7 @@ package android { field public static final int autoLink = 16842928; // 0x10100b0 field public static final int autoMirrored = 16843754; // 0x10103ea field public static final int autoRemoveFromRecents = 16843847; // 0x1010447 - field public static final int autoRevokePermissions = 16844308; // 0x1010614 + field public static final int autoRevokePermissions = 16844309; // 0x1010615 field public static final int autoSizeMaxTextSize = 16844102; // 0x1010546 field public static final int autoSizeMinTextSize = 16844088; // 0x1010538 field public static final int autoSizePresetSizes = 16844087; // 0x1010537 @@ -710,7 +709,7 @@ package android { field public static final int gravity = 16842927; // 0x10100af field public static final int gridViewStyle = 16842865; // 0x1010071 field public static final int groupIndicator = 16843019; // 0x101010b - field public static final int gwpAsanMode = 16844311; // 0x1010617 + field public static final int gwpAsanMode = 16844312; // 0x1010618 field public static final int hand_hour = 16843011; // 0x1010103 field public static final int hand_minute = 16843012; // 0x1010104 field public static final int handle = 16843354; // 0x101025a @@ -955,7 +954,7 @@ package android { field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad field public static final int mediaRouteTypes = 16843694; // 0x10103ae field public static final int menuCategory = 16843230; // 0x10101de - field public static final int mimeGroup = 16844310; // 0x1010616 + field public static final int mimeGroup = 16844311; // 0x1010617 field public static final int mimeType = 16842790; // 0x1010026 field public static final int min = 16844089; // 0x1010539 field public static final int minAspectRatio = 16844187; // 0x101059b @@ -1084,7 +1083,7 @@ package android { field public static final int preferenceScreenStyle = 16842891; // 0x101008b field public static final int preferenceStyle = 16842894; // 0x101008e field public static final int presentationTheme = 16843712; // 0x10103c0 - field public static final int preserveLegacyExternalStorage = 16844309; // 0x1010615 + field public static final int preserveLegacyExternalStorage = 16844310; // 0x1010616 field public static final int previewImage = 16843482; // 0x10102da field public static final int primaryContentAlpha = 16844114; // 0x1010552 field public static final int priority = 16842780; // 0x101001c @@ -45923,13 +45922,9 @@ package android.telecom { method public void onConference(android.telecom.Connection, android.telecom.Connection); method public void onConnectionServiceFocusGained(); method public void onConnectionServiceFocusLost(); - method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); - method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); - method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); - method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest); @@ -46419,14 +46414,26 @@ package android.telephony { field public static final int BAND_46 = 46; // 0x2e field public static final int BAND_47 = 47; // 0x2f field public static final int BAND_48 = 48; // 0x30 + field public static final int BAND_49 = 49; // 0x31 field public static final int BAND_5 = 5; // 0x5 + field public static final int BAND_50 = 50; // 0x32 + field public static final int BAND_51 = 51; // 0x33 + field public static final int BAND_52 = 52; // 0x34 + field public static final int BAND_53 = 53; // 0x35 field public static final int BAND_6 = 6; // 0x6 field public static final int BAND_65 = 65; // 0x41 field public static final int BAND_66 = 66; // 0x42 field public static final int BAND_68 = 68; // 0x44 field public static final int BAND_7 = 7; // 0x7 field public static final int BAND_70 = 70; // 0x46 + field public static final int BAND_71 = 71; // 0x47 + field public static final int BAND_72 = 72; // 0x48 + field public static final int BAND_73 = 73; // 0x49 + field public static final int BAND_74 = 74; // 0x4a field public static final int BAND_8 = 8; // 0x8 + field public static final int BAND_85 = 85; // 0x55 + field public static final int BAND_87 = 87; // 0x57 + field public static final int BAND_88 = 88; // 0x58 field public static final int BAND_9 = 9; // 0x9 } @@ -46490,7 +46497,13 @@ package android.telephony { field public static final int BAND_83 = 83; // 0x53 field public static final int BAND_84 = 84; // 0x54 field public static final int BAND_86 = 86; // 0x56 + field public static final int BAND_89 = 89; // 0x59 field public static final int BAND_90 = 90; // 0x5a + field public static final int BAND_91 = 91; // 0x5b + field public static final int BAND_92 = 92; // 0x5c + field public static final int BAND_93 = 93; // 0x5d + field public static final int BAND_94 = 94; // 0x5e + field public static final int BAND_95 = 95; // 0x5f } public static final class AccessNetworkConstants.UtranBand { @@ -47778,7 +47791,7 @@ package android.telephony { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); method public java.util.ArrayList<java.lang.String> divideMessage(String); - method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); + method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent); method @NonNull public android.os.Bundle getCarrierConfigValues(); method public static android.telephony.SmsManager getDefault(); method public static int getDefaultSmsSubscriptionId(); @@ -47788,7 +47801,7 @@ package android.telephony { method public int getSubscriptionId(); method public void injectSmsPdu(byte[], String, android.app.PendingIntent); method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent); - method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); + method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent); method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long); method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String); @@ -55753,8 +55766,8 @@ package android.view { } public final class WindowMetrics { - ctor public WindowMetrics(@NonNull android.util.Size, @NonNull android.view.WindowInsets); - method @NonNull public android.util.Size getSize(); + ctor public WindowMetrics(@NonNull android.graphics.Rect, @NonNull android.view.WindowInsets); + method @NonNull public android.graphics.Rect getBounds(); method @NonNull public android.view.WindowInsets getWindowInsets(); } diff --git a/api/system-current.txt b/api/system-current.txt index 7e25382f06d9..e5fd4d39e758 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -368,7 +368,6 @@ package android.app { method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int); field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; - field public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio"; field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot"; @@ -2030,10 +2029,10 @@ package android.content.pm { } public static class PackageInstaller.Session implements java.io.Closeable { - method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); + method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender); - method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams(); - method public void removeFile(int, @NonNull String); + method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams(); + method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String); } public static class PackageInstaller.SessionInfo implements android.os.Parcelable { @@ -2054,7 +2053,7 @@ package android.content.pm { public static class PackageInstaller.SessionParams implements android.os.Parcelable { method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean); method @Deprecated public void setAllowDowngrade(boolean); - method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); + method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams); method public void setDontKillApp(boolean); method public void setEnableRollback(boolean); method public void setEnableRollback(boolean, int); @@ -8395,12 +8394,12 @@ package android.os { public class RecoverySystem { method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException; - method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; + method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException; method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException; method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException; diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index 59544a971e5f..ca1d598ee7d7 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -32,8 +32,11 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; +import android.util.Pair; import java.io.FileDescriptor; +import java.util.ArrayList; +import java.util.List; /** * This class is a command line utility for manipulating content. A client @@ -72,7 +75,7 @@ public class Content { "usage: adb shell content [subcommand] [options]\n" + "\n" + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]" - + " --bind <BINDING> [--bind <BINDING>...]\n" + + " --bind <BINDING> [--bind <BINDING>...] [--extra <BINDING>...]\n" + " <URI> a content provider URI.\n" + " <BINDING> binds a typed value to a column and is formatted:\n" + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n" @@ -84,7 +87,8 @@ public class Content { + " adb shell content insert --uri content://settings/secure --bind name:s:new_setting" + " --bind value:s:new_value\n" + "\n" - + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n" + + "usage: adb shell content update --uri <URI> [--user <USER_ID>]" + + " [--where <WHERE>] [--extra <BINDING>...]\n" + " <WHERE> is a SQL style where clause in quotes (You have to escape single quotes" + " - see example below).\n" + " Example:\n" @@ -93,14 +97,15 @@ public class Content { + " value:s:newer_value --where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>" - + " [--bind <BINDING>...] [--where <WHERE>]\n" + + " [--bind <BINDING>...] [--where <WHERE>] [--extra <BINDING>...]\n" + " Example:\n" + " # Remove \"new_setting\" secure setting.\n" + " adb shell content delete --uri content://settings/secure " + "--where \"name=\'new_setting\'\"\n" + "\n" + "usage: adb shell content query --uri <URI> [--user <USER_ID>]" - + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n" + + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]" + + " [--extra <BINDING>...]\n" + " <PROJECTION> is a list of colon separated column names and is formatted:\n" + " <COLUMN_NAME>[:<COLUMN_NAME>...]\n" + " <SORT_ORDER> is the order in which rows in the result should be sorted.\n" @@ -196,6 +201,7 @@ public class Content { Uri uri = null; int userId = UserHandle.USER_SYSTEM; ContentValues values = new ContentValues(); + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg()) != null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); @@ -203,6 +209,8 @@ public class Content { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_BIND.equals(argument)) { parseBindValue(values); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -215,20 +223,23 @@ public class Content { throw new IllegalArgumentException("Bindings not specified." + " Did you specify --bind argument(s)?"); } - return new InsertCommand(uri, userId, values); + return new InsertCommand(uri, userId, values, extras); } private DeleteCommand parseDeleteCommand() { Uri uri = null; int userId = UserHandle.USER_SYSTEM; - String where = null; + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); } else if (ARGUMENT_USER.equals(argument)) { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_WHERE.equals(argument)) { - where = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, + argumentValueRequired(argument)); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -237,23 +248,26 @@ public class Content { throw new IllegalArgumentException("Content provider URI not specified." + " Did you specify --uri argument?"); } - return new DeleteCommand(uri, userId, where); + return new DeleteCommand(uri, userId, extras); } private UpdateCommand parseUpdateCommand() { Uri uri = null; int userId = UserHandle.USER_SYSTEM; - String where = null; ContentValues values = new ContentValues(); + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); } else if (ARGUMENT_USER.equals(argument)) { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_WHERE.equals(argument)) { - where = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, + argumentValueRequired(argument)); } else if (ARGUMENT_BIND.equals(argument)) { parseBindValue(values); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -266,7 +280,7 @@ public class Content { throw new IllegalArgumentException("Bindings not specified." + " Did you specify --bind argument(s)?"); } - return new UpdateCommand(uri, userId, values, where); + return new UpdateCommand(uri, userId, values, extras); } public CallCommand parseCallCommand() { @@ -274,7 +288,7 @@ public class Content { int userId = UserHandle.USER_SYSTEM; String arg = null; Uri uri = null; - ContentValues values = new ContentValues(); + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); @@ -285,11 +299,10 @@ public class Content { } else if (ARGUMENT_ARG.equals(argument)) { arg = argumentValueRequired(argument); } else if (ARGUMENT_EXTRA.equals(argument)) { - parseBindValue(values); + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } - } if (uri == null) { throw new IllegalArgumentException("Content provider URI not specified." @@ -298,7 +311,7 @@ public class Content { if (method == null) { throw new IllegalArgumentException("Content provider method not specified."); } - return new CallCommand(uri, userId, method, arg, values); + return new CallCommand(uri, userId, method, arg, extras); } private GetTypeCommand parseGetTypeCommand() { @@ -363,19 +376,22 @@ public class Content { Uri uri = null; int userId = UserHandle.USER_SYSTEM; String[] projection = null; - String sort = null; - String where = null; + Bundle extras = new Bundle(); for (String argument; (argument = mTokenizer.nextArg())!= null;) { if (ARGUMENT_URI.equals(argument)) { uri = Uri.parse(argumentValueRequired(argument)); } else if (ARGUMENT_USER.equals(argument)) { userId = Integer.parseInt(argumentValueRequired(argument)); } else if (ARGUMENT_WHERE.equals(argument)) { - where = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION, + argumentValueRequired(argument)); } else if (ARGUMENT_SORT.equals(argument)) { - sort = argumentValueRequired(argument); + extras.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, + argumentValueRequired(argument)); } else if (ARGUMENT_PROJECTION.equals(argument)) { projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*"); + } else if (ARGUMENT_EXTRA.equals(argument)) { + parseBindValue(extras); } else { throw new IllegalArgumentException("Unsupported argument: " + argument); } @@ -384,40 +400,76 @@ public class Content { throw new IllegalArgumentException("Content provider URI not specified." + " Did you specify --uri argument?"); } - return new QueryCommand(uri, userId, projection, where, sort); + return new QueryCommand(uri, userId, projection, extras); + } + + private List<String> splitWithEscaping(String argument, char splitChar) { + final List<String> res = new ArrayList<>(); + final StringBuilder cur = new StringBuilder(); + for (int i = 0; i < argument.length(); i++) { + char c = argument.charAt(i); + if (c == '\\') { + if (++i == argument.length()) { + throw new IllegalArgumentException("Invalid escaping"); + } else { + // Skip escaping char and insert next + c = argument.charAt(i); + cur.append(c); + } + } else if (c == splitChar) { + // Splitting char means next string + res.add(cur.toString()); + cur.setLength(0); + } else { + // Copy non-escaping and non-splitting char + cur.append(c); + } + } + res.add(cur.toString()); + return res; } - private void parseBindValue(ContentValues values) { + private Pair<String, Object> parseBindValue() { String argument = mTokenizer.nextArg(); if (TextUtils.isEmpty(argument)) { throw new IllegalArgumentException("Binding not well formed: " + argument); } - final int firstColonIndex = argument.indexOf(COLON); - if (firstColonIndex < 0) { + final List<String> split = splitWithEscaping(argument, COLON.charAt(0)); + if (split.size() != 3) { throw new IllegalArgumentException("Binding not well formed: " + argument); } - final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1); - if (secondColonIndex < 0) { - throw new IllegalArgumentException("Binding not well formed: " + argument); - } - String column = argument.substring(0, firstColonIndex); - String type = argument.substring(firstColonIndex + 1, secondColonIndex); - String value = argument.substring(secondColonIndex + 1); + String column = split.get(0); + String type = split.get(1); + String value = split.get(2); if (TYPE_STRING.equals(type)) { - values.put(column, value); + return Pair.create(column, value); } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) { - values.put(column, Boolean.parseBoolean(value)); - } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) { - values.put(column, Long.parseLong(value)); - } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) { - values.put(column, Double.parseDouble(value)); + return Pair.create(column, Boolean.parseBoolean(value)); + } else if (TYPE_INTEGER.equalsIgnoreCase(type)) { + return Pair.create(column, Integer.parseInt(value)); + } else if (TYPE_LONG.equalsIgnoreCase(type)) { + return Pair.create(column, Long.parseLong(value)); + } else if (TYPE_FLOAT.equalsIgnoreCase(type)) { + return Pair.create(column, Float.parseFloat(value)); + } else if (TYPE_DOUBLE.equalsIgnoreCase(type)) { + return Pair.create(column, Double.parseDouble(value)); } else if (TYPE_NULL.equalsIgnoreCase(type)) { - values.putNull(column); + return Pair.create(column, null); } else { throw new IllegalArgumentException("Unsupported type: " + type); } } + private void parseBindValue(ContentValues values) { + final Pair<String, Object> columnValue = parseBindValue(); + values.putObject(columnValue.first, columnValue.second); + } + + private void parseBindValue(Bundle extras) { + final Pair<String, Object> columnValue = parseBindValue(); + extras.putObject(columnValue.first, columnValue.second); + } + private String argumentValueRequired(String argument) { String value = mTokenizer.nextArg(); if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) { @@ -500,60 +552,43 @@ public class Content { private static class InsertCommand extends Command { final ContentValues mContentValues; + final Bundle mExtras; - public InsertCommand(Uri uri, int userId, ContentValues contentValues) { + public InsertCommand(Uri uri, int userId, ContentValues contentValues, Bundle extras) { super(uri, userId); mContentValues = contentValues; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { - provider.insert(resolveCallingPackage(), null, mUri, mContentValues, null); + provider.insert(resolveCallingPackage(), null, mUri, mContentValues, mExtras); } } private static class DeleteCommand extends Command { - final String mWhere; + final Bundle mExtras; - public DeleteCommand(Uri uri, int userId, String where) { + public DeleteCommand(Uri uri, int userId, Bundle extras) { super(uri, userId); - mWhere = where; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { - provider.delete(resolveCallingPackage(), null, mUri, - ContentResolver.createSqlQueryBundle(mWhere, null)); + provider.delete(resolveCallingPackage(), null, mUri, mExtras); } } private static class CallCommand extends Command { final String mMethod, mArg; - Bundle mExtras = null; + final Bundle mExtras; - public CallCommand(Uri uri, int userId, String method, String arg, ContentValues values) { + public CallCommand(Uri uri, int userId, String method, String arg, Bundle extras) { super(uri, userId); mMethod = method; mArg = arg; - if (values != null) { - mExtras = new Bundle(); - for (String key : values.keySet()) { - final Object val = values.get(key); - if (val instanceof String) { - mExtras.putString(key, (String) val); - } else if (val instanceof Float) { - mExtras.putFloat(key, (Float) val); - } else if (val instanceof Double) { - mExtras.putDouble(key, (Double) val); - } else if (val instanceof Boolean) { - mExtras.putBoolean(key, (Boolean) val); - } else if (val instanceof Integer) { - mExtras.putInt(key, (Integer) val); - } else if (val instanceof Long) { - mExtras.putLong(key, (Long) val); - } - } - } + mExtras = extras; } @Override @@ -604,21 +639,20 @@ public class Content { } } - private static class QueryCommand extends DeleteCommand { + private static class QueryCommand extends Command { final String[] mProjection; - final String mSortOrder; + final Bundle mExtras; - public QueryCommand( - Uri uri, int userId, String[] projection, String where, String sortOrder) { - super(uri, userId, where); + public QueryCommand(Uri uri, int userId, String[] projection, Bundle extras) { + super(uri, userId); mProjection = projection; - mSortOrder = sortOrder; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection, - ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null); + mExtras, null); if (cursor == null) { System.out.println("No result found."); return; @@ -670,18 +704,19 @@ public class Content { } } - private static class UpdateCommand extends InsertCommand { - final String mWhere; + private static class UpdateCommand extends Command { + final ContentValues mValues; + final Bundle mExtras; - public UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where) { - super(uri, userId, contentValues); - mWhere = where; + public UpdateCommand(Uri uri, int userId, ContentValues values, Bundle extras) { + super(uri, userId); + mValues = values; + mExtras = extras; } @Override public void onExecute(IContentProvider provider) throws Exception { - provider.update(resolveCallingPackage(), null, mUri, mContentValues, - ContentResolver.createSqlQueryBundle(mWhere, null)); + provider.update(resolveCallingPackage(), null, mUri, mValues, mExtras); } } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 6fe098c2a123..45f21ae8e3e1 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -97,6 +97,7 @@ cc_defaults { "src/stats_log_util.cpp", "src/statscompanion_util.cpp", "src/statsd_config.proto", + "src/statsd_metadata.proto", "src/StatsLogProcessor.cpp", "src/StatsService.cpp", "src/storage/StorageManager.cpp", diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f8b274979c17..979f950f2770 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -510,6 +510,7 @@ message Atom { VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"]; SimSlotState sim_slot_state = 10078 [(module) = "telephony"]; SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"]; + SettingSnapshot setting_snapshot = 10080 [(module) = "framework"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -2752,21 +2753,32 @@ message PhoneStateChanged { message BackGesture { enum BackType { - DEFAULT_BACK_TYPE = 0; - COMPLETED = 1; - COMPLETED_REJECTED = 2; // successful because coming from rejected area - INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area - INCOMPLETE = 4; + DEFAULT_BACK_TYPE = 0; + COMPLETED = 1; + COMPLETED_REJECTED = 2; // successful because coming from rejected area + INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area + INCOMPLETE = 4; // Unsuccessful, for reasons other than below. + INCOMPLETE_FAR_FROM_EDGE = 5; // Unsuccessful, far from the edge. + INCOMPLETE_MULTI_TOUCH = 6; // Unsuccessful, multi touch. + INCOMPLETE_LONG_PRESS = 7; // Unsuccessful, long press. + INCOMPLETE_VERTICAL_MOVE = 8; // Unsuccessful, move vertically. } optional BackType type = 1; - optional int32 y_coordinate = 2; // y coordinate for ACTION_DOWN event + optional int32 y_coordinate = 2 [deprecated = true]; // y coordinate for ACTION_DOWN event + optional int32 start_x = 4; // X coordinate for ACTION_DOWN event. + optional int32 start_y = 5; // Y coordinate for ACTION_DOWN event. + optional int32 end_x = 6; // X coordinate for ACTION_MOVE event. + optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event. + optional int32 left_boundary = 8; // left edge width + left inset + optional int32 right_boundary = 9; // screen width - (right edge width + right inset) + enum WindowHorizontalLocation { DEFAULT_LOCATION = 0; LEFT = 1; RIGHT = 2; } - optional WindowHorizontalLocation x_location = 3; + optional WindowHorizontalLocation x_location = 3 [deprecated = true]; } message ExclusionRectStateChanged { @@ -8999,3 +9011,37 @@ message RankingSelected { // Which of the ranked targets got picked, default starting position 0. optional int32 position_picked = 4; } + +/** + * Logs settings provider values. + * + * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider, + * then write the value to proto. + * + */ +message SettingSnapshot { + + // Setting key + optional string name = 1; + + enum SettingsValueType { + NOTASSIGNED = 0; + ASSIGNED_BOOL_TYPE = 1; + ASSIGNED_INT_TYPE = 2; + ASSIGNED_FLOAT_TYPE = 3; + ASSIGNED_STRING_TYPE = 4; + }; + // Setting value type + optional SettingsValueType type = 2; + + optional bool bool_value = 3; + + optional int32 int_value = 4; + + optional float float_value = 5; + + optional string str_value = 6; + + // Android user index. 0 for primary user, 10, 11 for secondary or profile user + optional int32 user_id = 7; +} diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 6f54ea7d86c2..fca48f96f56d 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -80,7 +80,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap, - mMetricIndexesWithActivation, mNoReportMetricIds); + mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds); mHashStringsInReport = config.hash_strings_in_metric_report(); mVersionStringsInReport = config.version_strings_in_metric_report(); diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 6d20822fd54c..7500ec91ce30 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -230,6 +230,10 @@ private: // Maps deactivation triggering event to MetricProducers. std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap; + // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers. + // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId. + std::unordered_map<int64_t, int> mAlertTrackerMap; + std::vector<int> mMetricIndexesWithActivation; void initLogSourceWhiteList(); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 40a313a14eab..e5fe87acc720 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -830,10 +830,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap, + unordered_map<int64_t, int>& alertTrackerMap, const sp<AlarmMonitor>& anomalyAlarmMonitor, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers) { - unordered_map<int64_t, int> anomalyTrackerMap; for (int i = 0; i < config.alert_size(); i++) { const Alert& alert = config.alert(i); const auto& itr = metricProducerMap.find(alert.metric_id()); @@ -858,7 +858,7 @@ bool initAlerts(const StatsdConfig& config, // The ALOGW for this invalid alert was already displayed in addAnomalyTracker(). return false; } - anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); + alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size())); allAnomalyTrackers.push_back(anomalyTracker); } for (int i = 0; i < config.subscription_size(); ++i) { @@ -872,8 +872,8 @@ bool initAlerts(const StatsdConfig& config, (long long)subscription.id()); return false; } - const auto& itr = anomalyTrackerMap.find(subscription.rule_id()); - if (itr == anomalyTrackerMap.end()) { + const auto& itr = alertTrackerMap.find(subscription.rule_id()); + if (itr == alertTrackerMap.end()) { ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"", (long long)subscription.id(), (long long)subscription.rule_id()); return false; @@ -944,6 +944,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& unordered_map<int, std::vector<int>>& trackerToConditionMap, unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + unordered_map<int64_t, int>& alertTrackerMap, vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) { unordered_map<int64_t, int> logTrackerMap; @@ -976,8 +977,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initMetricProducers failed"); return false; } - if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers, - allAnomalyTrackers)) { + if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor, + allMetricProducers, allAnomalyTrackers)) { ALOGE("initAlerts failed"); return false; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 5ebb232694a4..a8ccc6289b9a 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -128,6 +128,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& std::unordered_map<int, std::vector<int>>& trackerToConditionMap, unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::unordered_map<int64_t, int>& alertTrackerMap, vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds); diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto new file mode 100644 index 000000000000..e00fe33655ca --- /dev/null +++ b/cmds/statsd/src/statsd_metadata.proto @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.os.statsd.metadata; + +message ConfigKey { + optional int64 config_id = 1; + optional int32 uid = 2; +} + +message Field { + optional int32 tag = 1; + optional int32 field = 2; +} + +message FieldValue { + optional Field field = 1; + oneof value { + int32 value_int = 2; + int64 value_long = 3; + float value_float = 4; + double value_double = 5; + string value_str = 6; + bytes value_storage = 7; + } +} + +message MetricDimensionKey { + repeated FieldValue dimension_key_in_what = 1; + repeated FieldValue state_values_key = 2; +} + +message AlertMetadata { + optional int64 alert_id = 1; + // The earliest time the alert can be fired again in wall clock time. + optional int32 last_refractory_ends_sec = 2; + optional MetricDimensionKey dimension_key = 3; +} + +// All metadata for a config in statsd +message StatsMetadata { + optional ConfigKey config_key = 1; + repeated AlertMetadata alert_metadata = 2; +} + +message StatsMetadataList { + repeated StatsMetadata stats_metadata = 1; +}
\ No newline at end of file diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 71adc5789d92..356e40b9b99d 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -40,6 +40,7 @@ using android::os::statsd::Predicate; #ifdef __ANDROID__ const ConfigKey kConfigKey(0, 12345); +const long kAlertId = 3; const long timeBaseSec = 1000; @@ -85,7 +86,7 @@ StatsdConfig buildGoodConfig() { config.add_no_report_metric(3); auto alert = config.add_alert(); - alert->set_id(3); + alert->set_id(kAlertId); alert->set_metric_id(3); alert->set_num_buckets(10); alert->set_refractory_period_secs(100); @@ -218,7 +219,7 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { metric->mutable_dimensions_in_what()->add_child()->set_field(1); auto alert = config.add_alert(); - alert->set_id(103); + alert->set_id(kAlertId); alert->set_metric_id(3); alert->set_num_buckets(10); alert->set_refractory_period_secs(100); @@ -284,6 +285,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -293,10 +295,14 @@ TEST(MetricsManagerTest, TestGoodConfig) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); EXPECT_EQ(1u, allMetricProducers.size()); EXPECT_EQ(1u, allAnomalyTrackers.size()); EXPECT_EQ(1u, noReportMetricIds.size()); + EXPECT_EQ(1u, alertTrackerMap.size()); + EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end()); + EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0); } TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { @@ -316,6 +322,7 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -325,7 +332,8 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { @@ -345,6 +353,7 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -354,7 +363,8 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingMatchers) { @@ -374,6 +384,7 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, @@ -382,7 +393,8 @@ TEST(MetricsManagerTest, TestMissingMatchers) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, TestMissingPredicate) { @@ -402,6 +414,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, @@ -410,7 +423,7 @@ TEST(MetricsManagerTest, TestMissingPredicate) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, noReportMetricIds)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { @@ -430,6 +443,7 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -439,7 +453,8 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { @@ -459,6 +474,7 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> trackerToConditionMap; unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap; unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap; + unordered_map<int64_t, int> alertTrackerMap; vector<int> metricsWithActivation; std::set<int64_t> noReportMetricIds; @@ -468,7 +484,8 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, noReportMetricIds)); + alertTrackerMap, metricsWithActivation, + noReportMetricIds)); } #else diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 0f39efd51b58..e58bbb7893d7 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -13,14 +13,17 @@ // limitations under the License. #include "src/metrics/EventMetricProducer.h" -#include "metrics_test_helper.h" -#include "tests/statsd_test_util.h" #include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdio.h> + #include <vector> +#include "metrics_test_helper.h" +#include "stats_event.h" +#include "tests/statsd_test_util.h" + using namespace testing; using android::sp; using std::set; @@ -35,6 +38,22 @@ namespace statsd { const ConfigKey kConfigKey(0, 12345); +namespace { +void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeString(statsEvent, str.c_str()); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} +} // anonymous namespace + TEST(EventMetricProducerTest, TestNoCondition) { int64_t bucketStartTimeNs = 10000000000; int64_t eventStartTimeNs = bucketStartTimeNs + 1; @@ -43,8 +62,11 @@ TEST(EventMetricProducerTest, TestNoCondition) { EventMetric metric; metric.set_id(1); - LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1); - LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2); + LogEvent event1(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -54,8 +76,17 @@ TEST(EventMetricProducerTest, TestNoCondition) { eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - // TODO(b/110561136): get the report and check the content after the ProtoOutputStream change - // is done eventProducer.onDumpReport(); + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + EXPECT_EQ(2, report.event_metrics().data_size()); + EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); + EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos()); } TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { @@ -67,8 +98,11 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { metric.set_id(1); metric.set_condition(StringToId("SCREEN_ON")); - LogEvent event1(1, bucketStartTimeNs + 1); - LogEvent event2(1, bucketStartTimeNs + 10); + LogEvent event1(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1); + + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -81,51 +115,67 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); - // TODO: get the report and check the content after the ProtoOutputStream change is done. - // eventProducer.onDumpReport(); + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + EXPECT_EQ(1, report.event_metrics().data_size()); + EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos()); } -// TODO(b/149590301): Update this test to use new socket schema. -//TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { -// int64_t bucketStartTimeNs = 10000000000; -// int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; -// -// int tagId = 1; -// int conditionTagId = 2; -// -// EventMetric metric; -// metric.set_id(1); -// metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); -// MetricConditionLink* link = metric.add_links(); -// link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); -// buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); -// buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); -// -// LogEvent event1(tagId, bucketStartTimeNs + 1); -// EXPECT_TRUE(event1.write("111")); -// event1.init(); -// ConditionKey key1; -// key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "111")}; -// -// LogEvent event2(tagId, bucketStartTimeNs + 10); -// EXPECT_TRUE(event2.write("222")); -// event2.init(); -// ConditionKey key2; -// key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")}; -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); -// -// EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); -// -// EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); -// -// eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); -// eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); -// -// // TODO: get the report and check the content after the ProtoOutputStream change is done. -// // eventProducer.onDumpReport(); -//} +TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + + int tagId = 1; + int conditionTagId = 2; + + EventMetric metric; + metric.set_id(1); + metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON")); + MetricConditionLink* link = metric.add_links(); + link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID")); + buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what()); + buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition()); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111"); + ConditionKey key1; + key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "111")}; + + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222"); + ConditionKey key2; + key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = { + getMockedDimensionKey(conditionTagId, 2, "222")}; + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + // Condition is false for first event. + EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse)); + // Condition is true for second event. + EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue)); + + EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); + + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1); + eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2); + + // Check dump report content. + ProtoOutputStream output; + std::set<string> strSet; + eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/, + true /*erase data*/, FAST, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_event_metrics()); + EXPECT_EQ(1, report.event_metrics().data_size()); + EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 609324e91daa..d372ffd7e689 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -26,6 +26,7 @@ #include "src/matchers/SimpleLogMatchingTracker.h" #include "src/metrics/MetricProducer.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "tests/statsd_test_util.h" using namespace testing; @@ -53,6 +54,28 @@ const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; +namespace { +shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1, + int32_t value2) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); + + AStatsEvent_writeInt32(statsEvent, value1); + AStatsEvent_writeString(statsEvent, str1.c_str()); + AStatsEvent_writeInt32(statsEvent, value2); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); + + return logEvent; +} +} // anonymous namespace + /* * Tests that the first bucket works correctly */ @@ -88,769 +111,685 @@ TEST(GaugeMetricProducerTest, TestFirstBucket) { EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs()); } -// TODO(b/149590301): Update these tests to use new socket schema. -//TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.mutable_gauge_fields_filter()->set_include_all(false); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(1); -// gaugeFieldMatcher->add_child()->set_field(3); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(3); -// event->write("some value"); -// event->write(11); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// allData.clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); -// event->write(10); -// event->write("some value"); -// event->write(11); -// event->init(); -// allData.push_back(event); -// -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(10, it->mValue.int_value); -// it++; -// EXPECT_EQ(11, it->mValue.int_value); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms -// .front().mFields->begin()->mValue.int_value); -// -// allData.clear(); -// std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10); -// event2->write(24); -// event2->write("some value"); -// event2->write(25); -// event2->init(); -// allData.push_back(event2); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(24, it->mValue.int_value); -// it++; -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(25, it->mValue.int_value); -// // One dimension. -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); -// it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(10L, it->mValue.int_value); -// it++; -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(11L, it->mValue.int_value); -// -// gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); -// EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); -// // One dimension. -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); -// it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(24L, it->mValue.int_value); -// it++; -// EXPECT_EQ(INT, it->mValue.getType()); -// EXPECT_EQ(25L, it->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { -// sp<AlarmMonitor> alarmMonitor; -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.mutable_gauge_fields_filter()->set_include_all(true); -// -// Alert alert; -// alert.set_id(101); -// alert.set_metric_id(metricId); -// alert.set_trigger_if_sum_gt(25); -// alert.set_num_buckets(100); -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, -// bucketStartTimeNs, pullerManager); -// -// sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); -// EXPECT_TRUE(anomalyTracker != nullptr); -// -// shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event1->write(1); -// event1->write(10); -// event1->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); -// EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); -// -// gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// // Partial buckets are not sent to anomaly tracker. -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Create an event in the same partial bucket. -// shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC); -// event2->write(1); -// event2->write(10); -// event2->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// // Partial buckets are not sent to anomaly tracker. -// EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Next event should trigger creation of new bucket and send previous full bucket to anomaly -// // tracker. -// shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC); -// event3->write(1); -// event3->write(10); -// event3->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); -// EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -// -// // Next event should trigger creation of new bucket. -// shared_ptr<LogEvent> event4 = -// make_shared<LogEvent>(tagId, bucketStartTimeNs + 125 * NS_PER_SEC); -// event4->write(1); -// event4->write(10); -// event4->init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); -// EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = -// new EventMatcherWizard({new SimpleLogMatchingTracker( -// atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Return(false)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs); -// event->write("some value"); -// event->write(2); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, -// bucketStartTimeNs, bucketStartTimeNs, pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); -// event->write("some value"); -// event->write(1); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// -// gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// -// allData.clear(); -// event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1); -// event->write("some value"); -// event->write(3); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// metric.set_split_bucket_for_app_upgrade(false); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); -// event->write("some value"); -// event->write(1); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// -// gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); -// EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); -// EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); -// EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// metric.set_condition(StringToId("SCREEN_ON")); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write("some value"); -// event->write(100); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, -// logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, -// bucketStartTimeNs, bucketStartTimeNs, pullerManager); -// -// gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); -// -// vector<shared_ptr<LogEvent>> allData; -// allData.clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); -// event->write("some value"); -// event->write(110); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); -// -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms.front() -// .mFields->begin() -// ->mValue.int_value); -// -// gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); -// gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); -// EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms.front() -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { -// const int conditionTag = 65; -// GaugeMetric metric; -// metric.set_id(1111111); -// metric.set_bucket(ONE_MINUTE); -// metric.mutable_gauge_fields_filter()->set_include_all(true); -// metric.set_condition(StringToId("APP_DIED")); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto dim = metric.mutable_dimensions_in_what(); -// dim->set_field(tagId); -// dim->add_child()->set_field(1); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// EXPECT_CALL(*wizard, query(_, _, _)) -// .WillRepeatedly( -// Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, -// const bool isPartialLink) { -// int pos[] = {1, 0, 0}; -// Field f(conditionTag, pos, 0); -// HashableDimensionKey key; -// key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); -// -// return ConditionState::kTrue; -// })); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(1000); -// event->write(100); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, -// logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, -// bucketStartTimeNs, bucketStartTimeNs, pullerManager); -// -// gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); -// -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; -// EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); -// EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); -// -// EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); -// -// vector<shared_ptr<LogEvent>> allData; -// allData.clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); -// event->write(1000); -// event->write(110); -// event->init(); -// allData.push_back(event); -// gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); -// -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -//} -// -//TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { -// sp<AlarmMonitor> alarmMonitor; -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); -// -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(2); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// Alert alert; -// alert.set_id(101); -// alert.set_metric_id(metricId); -// alert.set_trigger_if_sum_gt(25); -// alert.set_num_buckets(2); -// const int32_t refPeriodSec = 60; -// alert.set_refractory_period_secs(refPeriodSec); -// sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); -// -// int tagId = 1; -// std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); -// event1->write("some value"); -// event1->write(13); -// event1->init(); -// -// gaugeProducer.onDataPulled({event1}, /** succeed */ true, bucketStartTimeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); -// -// std::shared_ptr<LogEvent> event2 = -// std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20); -// event2->write("some value"); -// event2->write(15); -// event2->init(); -// -// gaugeProducer.onDataPulled({event2}, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); -// -// std::shared_ptr<LogEvent> event3 = -// std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10); -// event3->write("some value"); -// event3->write(26); -// event3->init(); -// -// gaugeProducer.onDataPulled({event3}, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() -// ->second.front() -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), -// std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); -// -// // The event4 does not have the gauge field. Thus the current bucket value is 0. -// std::shared_ptr<LogEvent> event4 = -// std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10); -// event4->write("some value"); -// event4->init(); -// gaugeProducer.onDataPulled({event4}, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); -//} -// -//TEST(GaugeMetricProducerTest, TestPullOnTrigger) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); -// metric.mutable_gauge_fields_filter()->set_include_all(false); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); -// gaugeFieldMatcher->set_field(tagId); -// gaugeFieldMatcher->add_child()->set_field(1); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(4); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); -// event->write(5); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Return(true)); -// -// int triggerId = 5; -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// -// EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); -// LogEvent trigger(triggerId, bucketStartTimeNs + 10); -// trigger.init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucketStartTimeNs + 20); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// -// EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); -// EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms[0] -// .mFields->begin() -// ->mValue.int_value); -// EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() -// ->second.back() -// .mGaugeAtoms[1] -// .mFields->begin() -// ->mValue.int_value); -//} -// -//TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(ONE_MINUTE); -// metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); -// metric.mutable_gauge_fields_filter()->set_include_all(true); -// metric.set_max_pull_delay_sec(INT_MAX); -// auto dimensionMatcher = metric.mutable_dimensions_in_what(); -// // use field 1 as dimension. -// dimensionMatcher->set_field(tagId); -// dimensionMatcher->add_child()->set_field(1); -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3); -// event->write(3); -// event->write(4); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); -// event->write(4); -// event->write(5); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); -// event->write(4); -// event->write(6); -// event->init(); -// data->push_back(event); -// return true; -// })) -// .WillOnce(Return(true)); -// -// int triggerId = 5; -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// vector<shared_ptr<LogEvent>> allData; -// -// LogEvent trigger(triggerId, bucketStartTimeNs + 3); -// trigger.init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); -// trigger.setElapsedTimestampNs(bucketStartTimeNs + 10); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); -// EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucketStartTimeNs + 20); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); -// trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// -// EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size()); -// auto bucketIt = gaugeProducer.mPastBuckets.begin(); -// EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); -// EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); -// EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); -// bucketIt++; -// EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); -// EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); -// EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); -// EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); -//} -// -///* -// * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size -// * is smaller than the "min_bucket_size_nanos" specified in the metric config. -// */ -//TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { -// GaugeMetric metric; -// metric.set_id(metricId); -// metric.set_bucket(FIVE_MINUTES); -// metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); -// metric.set_min_bucket_size_nanos(10000000000); // 10 seconds -// -// sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); -// -// UidMap uidMap; -// SimpleAtomMatcher atomMatcher; -// atomMatcher.set_atom_id(tagId); -// sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ -// new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); -// -// sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); -// EXPECT_CALL(*pullerManager, Pull(tagId, _)) -// // Bucket start. -// .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { -// data->clear(); -// shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); -// event->write("field1"); -// event->write(10); -// event->init(); -// data->push_back(event); -// return true; -// })); -// -// int triggerId = 5; -// GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, -// logEventMatcherIndex, eventMatcherWizard, -// tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, -// pullerManager); -// -// LogEvent trigger(triggerId, bucketStartTimeNs + 3); -// trigger.init(); -// gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); -// -// // Check dump report. -// ProtoOutputStream output; -// std::set<string> strSet; -// gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, -// true, FAST /* dump_latency */, &strSet, &output); -// -// StatsLogReport report = outputStreamToProto(&output); -// EXPECT_TRUE(report.has_gauge_metrics()); -// EXPECT_EQ(0, report.gauge_metrics().data_size()); -// EXPECT_EQ(1, report.gauge_metrics().skipped_size()); -// -// EXPECT_EQ(NanoToMillis(bucketStartTimeNs), -// report.gauge_metrics().skipped(0).start_bucket_elapsed_millis()); -// EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), -// report.gauge_metrics().skipped(0).end_bucket_elapsed_millis()); -// EXPECT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size()); -// -// auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0); -// EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); -// EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); -//} +TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(false); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(1); + gaugeFieldMatcher->add_child()->set_field(3); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(makeLogEvent(tagId, bucketStartTimeNs + 10, 3, "some value", 11)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11)); + + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10, it->mValue.int_value); + it++; + EXPECT_EQ(11, it->mValue.int_value); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); + + allData.clear(); + allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24, it->mValue.int_value); + it++; + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25, it->mValue.int_value); + // One dimension. + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); + it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(10L, it->mValue.int_value); + it++; + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(11L, it->mValue.int_value); + + gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); + EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); + // One dimension. + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); + it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(24L, it->mValue.int_value); + it++; + EXPECT_EQ(INT, it->mValue.getType()); + EXPECT_EQ(25L, it->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { + sp<AlarmMonitor> alarmMonitor; + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(true); + + Alert alert; + alert.set_id(101); + alert.set_metric_id(metricId); + alert.set_trigger_if_sum_gt(25); + alert.set_num_buckets(100); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, + -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); + EXPECT_TRUE(anomalyTracker != nullptr); + + LogEvent event1(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + // Partial buckets are not sent to anomaly tracker. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Create an event in the same partial bucket. + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + // Partial buckets are not sent to anomaly tracker. + EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Next event should trigger creation of new bucket and send previous full bucket to anomaly + // tracker. + LogEvent event3(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); + + // Next event should trigger creation of new bucket. + LogEvent event4(/*uid=*/0, /*pid=*/0); + CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); + EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); +} + +TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Return(false)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, eventUpgradeTimeNs, 2)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + metric.set_split_bucket_for_app_upgrade(false); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); + EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + metric.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 100)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); + + gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); + gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms.front() + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { + const int conditionTag = 65; + GaugeMetric metric; + metric.set_id(1111111); + metric.set_bucket(ONE_MINUTE); + metric.mutable_gauge_fields_filter()->set_include_all(true); + metric.set_condition(StringToId("APP_DIED")); + metric.set_max_pull_delay_sec(INT_MAX); + auto dim = metric.mutable_dimensions_in_what(); + dim->set_field(tagId); + dim->add_child()->set_field(1); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EXPECT_CALL(*wizard, query(_, _, _)) + .WillRepeatedly( + Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, + const bool isPartialLink) { + int pos[] = {1, 0, 0}; + Field f(conditionTag, pos, 0); + HashableDimensionKey key; + key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); + + return ConditionState::kTrue; + })); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 1000, 100)); + return true; + })); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + + gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); + + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; + EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); + EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + + EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); + + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); +} + +TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { + sp<AlarmMonitor> alarmMonitor; + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); + + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + Alert alert; + alert.set_id(101); + alert.set_metric_id(metricId); + alert.set_trigger_if_sum_gt(25); + alert.set_num_buckets(2); + const int32_t refPeriodSec = 60; + alert.set_refractory_period_secs(refPeriodSec); + sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); + + int tagId = 1; + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); + + std::shared_ptr<LogEvent> event2 = + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15); + + allData.clear(); + allData.push_back(event2); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); + + allData.clear(); + allData.push_back( + CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() + ->second.front() + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), + std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); + + // This event does not have the gauge field. Thus the current bucket value is 0. + allData.clear(); + allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10)); + gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); +} + +TEST(GaugeMetricProducerTest, TestPullOnTrigger) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); + metric.mutable_gauge_fields_filter()->set_include_all(false); + metric.set_max_pull_delay_sec(INT_MAX); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(1); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 4)); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 5)); + return true; + })) + .WillOnce(Return(true)); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); + EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms[0] + .mFields->begin() + ->mValue.int_value); + EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() + ->second.back() + .mGaugeAtoms[1] + .mFields->begin() + ->mValue.int_value); +} + +TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); + metric.mutable_gauge_fields_filter()->set_include_all(true); + metric.set_max_pull_delay_sec(INT_MAX); + auto dimensionMatcher = metric.mutable_dimensions_in_what(); + // use field 1 as dimension. + dimensionMatcher->set_field(tagId); + dimensionMatcher->add_child()->set_field(1); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 3, 3, 4)); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 4, 5)); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 4, 6)); + return true; + })) + .WillOnce(Return(true)); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); + EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); + triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size()); + auto bucketIt = gaugeProducer.mPastBuckets.begin(); + EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); + EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); + EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); + bucketIt++; + EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); + EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); + EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); + EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); +} + +/* + * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size + * is smaller than the "min_bucket_size_nanos" specified in the metric config. + */ +TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) { + GaugeMetric metric; + metric.set_id(metricId); + metric.set_bucket(FIVE_MINUTES); + metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); + metric.set_min_bucket_size_nanos(10000000000); // 10 seconds + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Bucket start. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 10)); + return true; + })); + + int triggerId = 5; + GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, + tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + + LogEvent triggerEvent(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3); + gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent); + + // Check dump report. + ProtoOutputStream output; + std::set<string> strSet; + gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true, + FAST /* dump_latency */, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_gauge_metrics()); + EXPECT_EQ(0, report.gauge_metrics().data_size()); + EXPECT_EQ(1, report.gauge_metrics().skipped_size()); + + EXPECT_EQ(NanoToMillis(bucketStartTimeNs), + report.gauge_metrics().skipped(0).start_bucket_elapsed_millis()); + EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), + report.gauge_metrics().skipped(0).end_bucket_elapsed_millis()); + EXPECT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size()); + + auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0); + EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason()); + EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis()); +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index c7838fcddb53..8c8836b94f56 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -428,7 +428,7 @@ shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int return logEvent; } -// + void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, int32_t value2) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -531,6 +531,18 @@ shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { return logEvent; } +void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) { + AStatsEvent* statsEvent = AStatsEvent_obtain(); + AStatsEvent_setAtomId(statsEvent, atomId); + AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + AStatsEvent_release(statsEvent); +} + std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state) { AStatsEvent* statsEvent = AStatsEvent_obtain(); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 05e1572e3aa9..7c017554d511 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -187,6 +187,8 @@ void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTi std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs); +void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs); + // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 26db8f3ecb54..a5dcefcf3ab7 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1088,8 +1088,9 @@ public class AppOpsManager { public static final int OP_ACTIVATE_PLATFORM_VPN = AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN; /** @hide */ public static final int OP_LOADER_USAGE_STATS = AppProtoEnums.APP_OP_LOADER_USAGE_STATS; - /** @hide Access telephony call audio */ - public static final int OP_ACCESS_CALL_AUDIO = AppProtoEnums.APP_OP_ACCESS_CALL_AUDIO; + + // App op deprecated/removed. + private static final int OP_DEPRECATED_1 = AppProtoEnums.APP_OP_DEPRECATED_1; /** @hide Auto-revoke app permissions if app is unused for an extended period */ public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = @@ -1396,9 +1397,6 @@ public class AppOpsManager { @SystemApi public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; - /** @hide Access telephony call audio */ - @SystemApi - public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio"; /** @hide Auto-revoke app permissions if app is unused for an extended period */ @SystemApi @@ -1498,7 +1496,6 @@ public class AppOpsManager { OP_MANAGE_EXTERNAL_STORAGE, OP_INTERACT_ACROSS_PROFILES, OP_LOADER_USAGE_STATS, - OP_ACCESS_CALL_AUDIO, OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, }; @@ -1608,7 +1605,7 @@ public class AppOpsManager { OP_INTERACT_ACROSS_PROFILES, //INTERACT_ACROSS_PROFILES OP_ACTIVATE_PLATFORM_VPN, // ACTIVATE_PLATFORM_VPN OP_LOADER_USAGE_STATS, // LOADER_USAGE_STATS - OP_ACCESS_CALL_AUDIO, // ACCESS_CALL_AUDIO + OP_DEPRECATED_1, // deprecated OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -1713,7 +1710,7 @@ public class AppOpsManager { OPSTR_INTERACT_ACROSS_PROFILES, OPSTR_ACTIVATE_PLATFORM_VPN, OPSTR_LOADER_USAGE_STATS, - OPSTR_ACCESS_CALL_AUDIO, + "", // deprecated OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER, }; @@ -1819,7 +1816,7 @@ public class AppOpsManager { "INTERACT_ACROSS_PROFILES", "ACTIVATE_PLATFORM_VPN", "LOADER_USAGE_STATS", - "ACCESS_CALL_AUDIO", + "deprecated", "AUTO_REVOKE_PERMISSIONS_IF_UNUSED", "AUTO_REVOKE_MANAGED_BY_INSTALLER", }; @@ -1926,7 +1923,7 @@ public class AppOpsManager { android.Manifest.permission.INTERACT_ACROSS_PROFILES, null, // no permission for OP_ACTIVATE_PLATFORM_VPN android.Manifest.permission.LOADER_USAGE_STATS, - Manifest.permission.ACCESS_CALL_AUDIO, + null, // deprecated operation null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2033,7 +2030,7 @@ public class AppOpsManager { null, // INTERACT_ACROSS_PROFILES null, // ACTIVATE_PLATFORM_VPN null, // LOADER_USAGE_STATS - null, // ACCESS_CALL_AUDIO + null, // deprecated operation null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2139,7 +2136,7 @@ public class AppOpsManager { null, // INTERACT_ACROSS_PROFILES null, // ACTIVATE_PLATFORM_VPN null, // LOADER_USAGE_STATS - null, // ACCESS_CALL_AUDIO + null, // deprecated operation null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED null, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2244,7 +2241,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS - AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO + AppOpsManager.MODE_IGNORED, // deprecated operation AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -2353,7 +2350,7 @@ public class AppOpsManager { false, // INTERACT_ACROSS_PROFILES false, // ACTIVATE_PLATFORM_VPN false, // LOADER_USAGE_STATS - false, // ACCESS_CALL_AUDIO + false, // deprecated operation false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED false, // AUTO_REVOKE_MANAGED_BY_INSTALLER }; @@ -8446,7 +8443,9 @@ public class AppOpsManager { /** * Pulls current AppOps access report and picks package and op to watch for next access report - * + * Returns null if no reports were collected since last call. There is no guarantee of report + * collection, hence this method should be called periodically even if no report was collected + * to pick different package and op to watch. * @hide */ @SystemApi diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index 61be01f9f6c0..0ecc003a33bd 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -499,9 +499,11 @@ public final class ApplicationExitInfo implements Parcelable { /** * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and - * {@link #getPackageUid}, if an external service was bound with the flag - * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here - * will be the kernel user identifier of the external service provider. + * {@link #getPackageUid}, if an external service has the + * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set + * to <code>true<code> and was bound with the flag + * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will + * be the kernel user identifier of the external service provider. */ public int getDefiningUid() { return mDefiningUid; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9ccfe8df14c2..17fd4efec9cc 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2414,8 +2414,7 @@ class ContextImpl extends Context { : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; final List<ResourcesLoader> loaders = mResources.getLoaders(); - // TODO(b/128338354): Rename to createTokenResources - return mResourcesManager.createBaseActivityResources(mToken, resDir, splitResDirs, + return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs, overlayDirs, libDirs, displayId, null /* overrideConfig */, compatInfo, mClassLoader, loaders); } @@ -2684,7 +2683,7 @@ class ContextImpl extends Context { // Create the base resources for which all configuration contexts for this Activity // will be rebased upon. - context.setResources(resourcesManager.createBaseActivityResources(activityToken, + context.setResources(resourcesManager.createBaseTokenResources(activityToken, packageInfo.getResDir(), splitDirs, packageInfo.getOverlayDirs(), diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index b8221b4efa2f..833bfed573b2 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -99,6 +99,7 @@ interface IActivityManager { void unregisterUidObserver(in IUidObserver observer); boolean isUidActive(int uid, String callingPackage); int getUidProcessState(int uid, in String callingPackage); + boolean isUidActiveOrForeground(int uid, String callingPackage); // =============== End of transactions used on native side as well ============================ // Special low-level communication with activity manager. @@ -143,9 +144,6 @@ interface IActivityManager { void attachApplication(in IApplicationThread app, long startSeq); List<ActivityManager.RunningTaskInfo> getTasks(int maxNum); @UnsupportedAppUsage - List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType, - int ignoreWindowingMode); - @UnsupportedAppUsage void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task, int flags, in Bundle options); @UnsupportedAppUsage diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 03717ecd4038..8b8ebe80f01f 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -155,8 +155,8 @@ interface IActivityTaskManager { boolean removeTask(int taskId); void removeAllVisibleRecentTasks(); List<ActivityManager.RunningTaskInfo> getTasks(int maxNum); - List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType, - int ignoreWindowingMode); + List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, + boolean filterOnlyVisibleRecents); boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, in Intent resultData); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 5f756033390b..392c05ac5949 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -695,26 +695,26 @@ public class ResourcesManager { } /** - * Creates base resources for an Activity. Calls to + * Creates base resources for a binder token. Calls to * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration, - * CompatibilityInfo, ClassLoader, List)} with the same activityToken will have their override + * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override * configurations merged with the one specified here. * - * @param activityToken Represents an Activity. + * @param token Represents an {@link Activity} or {@link WindowContext}. * @param resDir The base resource path. Can be null (only framework resources will be loaded). * @param splitResDirs An array of split resource paths. Can be null. * @param overlayDirs An array of overlay paths. Can be null. * @param libDirs An array of resource library paths. Can be null. * @param displayId The ID of the display for which to create the resources. * @param overrideConfig The configuration to apply on top of the base configuration. Can be - * null. This provides the base override for this Activity. + * {@code null}. This provides the base override for this token. * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is * {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}. * @param classLoader The class loader to use when inflating Resources. If null, the * {@link ClassLoader#getSystemClassLoader()} is used. * @return a Resources object from which to access resources. */ - public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken, + public @Nullable Resources createBaseTokenResources(@NonNull IBinder token, @Nullable String resDir, @Nullable String[] splitResDirs, @Nullable String[] overlayDirs, @@ -739,24 +739,24 @@ public class ResourcesManager { classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader(); if (DEBUG) { - Slog.d(TAG, "createBaseActivityResources activity=" + activityToken + Slog.d(TAG, "createBaseActivityResources activity=" + token + " with key=" + key); } synchronized (this) { // Force the creation of an ActivityResourcesStruct. - getOrCreateActivityResourcesStructLocked(activityToken); + getOrCreateActivityResourcesStructLocked(token); } // Update any existing Activity Resources references. - updateResourcesForActivity(activityToken, overrideConfig, displayId, + updateResourcesForActivity(token, overrideConfig, displayId, false /* movedToDifferentDisplay */); - cleanupReferences(activityToken); - rebaseKeyForActivity(activityToken, key); + cleanupReferences(token); + rebaseKeyForActivity(token, key); synchronized (this) { - Resources resources = findResourcesForActivityLocked(activityToken, key, + Resources resources = findResourcesForActivityLocked(token, key, classLoader); if (resources != null) { return resources; @@ -764,7 +764,7 @@ public class ResourcesManager { } // Now request an actual Resources object. - return createResources(activityToken, key, classLoader); + return createResources(token, key, classLoader); } finally { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); } diff --git a/core/java/android/app/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java index 5ebcc46aa0d8..b8ad30840173 100644 --- a/core/java/android/app/TaskEmbedder.java +++ b/core/java/android/app/TaskEmbedder.java @@ -16,6 +16,7 @@ package android.app; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; @@ -291,7 +292,7 @@ public class TaskEmbedder { * @see #startActivity(PendingIntent) */ public void startActivity(@NonNull Intent intent) { - final ActivityOptions options = prepareActivityOptions(); + final ActivityOptions options = prepareActivityOptions(null); mContext.startActivity(intent, options.toBundle()); } @@ -304,7 +305,7 @@ public class TaskEmbedder { * @see #startActivity(PendingIntent) */ public void startActivity(@NonNull Intent intent, UserHandle user) { - final ActivityOptions options = prepareActivityOptions(); + final ActivityOptions options = prepareActivityOptions(null); mContext.startActivityAsUser(intent, options.toBundle(), user); } @@ -316,7 +317,7 @@ public class TaskEmbedder { * @see #startActivity(Intent) */ public void startActivity(@NonNull PendingIntent pendingIntent) { - final ActivityOptions options = prepareActivityOptions(); + final ActivityOptions options = prepareActivityOptions(null); try { pendingIntent.send(null /* context */, 0 /* code */, null /* intent */, null /* onFinished */, null /* handler */, null /* requiredPermission */, @@ -337,8 +338,7 @@ public class TaskEmbedder { */ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, @NonNull ActivityOptions options) { - - options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId()); + prepareActivityOptions(options); try { pendingIntent.send(mContext, 0 /* code */, fillInIntent, null /* onFinished */, null /* handler */, null /* requiredPermission */, @@ -364,21 +364,25 @@ public class TaskEmbedder { @NonNull ActivityOptions options, @Nullable Rect sourceBounds) { LauncherApps service = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE); - options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId()); + prepareActivityOptions(options); service.startShortcut(shortcut, sourceBounds, options.toBundle()); } /** - * Check if container is ready to launch and create {@link ActivityOptions} to target the - * virtual display. + * Check if container is ready to launch and modify {@param options} to target the virtual + * display, creating them if necessary. */ - private ActivityOptions prepareActivityOptions() { + private ActivityOptions prepareActivityOptions(ActivityOptions options) { if (mVirtualDisplay == null) { throw new IllegalStateException( "Trying to start activity before ActivityView is ready."); } - final ActivityOptions options = ActivityOptions.makeBasic(); + if (options == null) { + options = ActivityOptions.makeBasic(); + } options.setLaunchDisplayId(getDisplayId()); + options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + options.setTaskAlwaysOnTop(true); return options; } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c4458b30c2db..10309a9b4a03 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -4424,11 +4424,13 @@ public class DevicePolicyManager { * the current factory reset protection (FRP) policy set previously by * {@link #setFactoryResetProtectionPolicy}. * <p> - * This method can also be called by the FRP management agent on device, in which case, - * it can pass {@code null} as the ComponentName. + * This method can also be called by the FRP management agent on device or with the permission + * {@link android.Manifest.permission#MASTER_CLEAR}, in which case, it can pass {@code null} + * as the ComponentName. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with or - * {@code null} if called by the FRP management agent on device. + * {@code null} if called by the FRP management agent on device or with the + * permission {@link android.Manifest.permission#MASTER_CLEAR}. * @return The current FRP policy object or {@code null} if no policy is set. * @throws SecurityException if {@code admin} is not a device owner, a profile owner of * an organization-owned device or the FRP management agent. diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java index 954db0459f99..aa94e817c152 100644 --- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java +++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java @@ -43,6 +43,12 @@ import java.util.List; * reset protection policy for the device by calling the {@code DevicePolicyManager} method * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName, * FactoryResetProtectionPolicy)}}. + * <p> + * Normally factory reset protection does not kick in if the device is factory reset via Settings. + * This is also the case when a device owner sets factory reset protection policy. However, + * when a profile owner of an organization-owned device sets factory reset protection policy that + * locks the device to specific accounts, the policy will take effect even if factory reset is + * performed from Settings. * * @see DevicePolicyManager#setFactoryResetProtectionPolicy * @see DevicePolicyManager#getFactoryResetProtectionPolicy @@ -236,4 +242,16 @@ public final class FactoryResetProtectionPolicy implements Parcelable { } } + /** + * Returns if the policy will result in factory reset protection being locked to + * admin-specified accounts. + * <p> + * When a device has a non-empty factory reset protection policy, trusted factory reset + * via Settings will no longer remove factory reset protection from the device. + * @hide + */ + public boolean isNotEmpty() { + return !mFactoryResetProtectionAccounts.isEmpty() && mFactoryResetProtectionEnabled; + } + } diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 31c3232f4714..9cf6569a6220 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -307,19 +307,7 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall try { result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri)); } catch (Exception e) { - Parcel parcel = Parcel.obtain(); - try { - try { - parcel.writeException(e); - } catch (Exception ex) { - // getType threw an unparcelable exception. Wrap the message into - // a parcelable exception type - parcel.writeException(new IllegalStateException(e.getMessage())); - } - result.putByteArray(ContentResolver.REMOTE_CALLBACK_ERROR, parcel.marshall()); - } finally { - parcel.recycle(); - } + putExceptionInBundle(result, ContentResolver.REMOTE_CALLBACK_ERROR, e); } callback.sendResult(result); } @@ -602,8 +590,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri, RemoteCallback callback) { final Bundle result = new Bundle(); - result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, - canonicalize(callingPkg, attributionTag, uri)); + try { + result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT, + canonicalize(callingPkg, attributionTag, uri)); + } catch (Exception e) { + putExceptionInBundle(result, ContentResolver.REMOTE_CALLBACK_ERROR, e); + } callback.sendResult(result); } @@ -717,6 +709,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return AppOpsManager.MODE_ALLOWED; } + + private void putExceptionInBundle(Bundle bundle, String key, Exception e) { + Parcel parcel = Parcel.obtain(); + try { + try { + parcel.writeException(e); + } catch (Exception ex) { + // getType threw an unparcelable exception. Wrap the message into + // a parcelable exception type + parcel.writeException(new IllegalStateException(e.getMessage())); + } + bundle.putByteArray(key, parcel.marshall()); + } finally { + parcel.recycle(); + } + } } boolean checkUser(int pid, int uid, Context context) { diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 6ba811e077ac..7578ede2648d 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -487,6 +487,34 @@ public class CrossProfileApps { } } + /** + * Clears the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} back to + * its default value for every package on the device. + * + * <p>This method can be used to ensure that app-op state is not left around on existing users + * for previously-configured profiles. + * + * <p>If the caller does not have the {@link android.Manifest.permission + * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that + * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String, + * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}. + * + * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link + * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. + * + * @hide + */ + @RequiresPermission( + allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, + android.Manifest.permission.INTERACT_ACROSS_USERS}) + public void clearInteractAcrossProfilesAppOps() { + try { + mService.clearInteractAcrossProfilesAppOps(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl index 9b0dae221538..e2850f111c4f 100644 --- a/core/java/android/content/pm/ICrossProfileApps.aidl +++ b/core/java/android/content/pm/ICrossProfileApps.aidl @@ -40,4 +40,5 @@ interface ICrossProfileApps { boolean canConfigureInteractAcrossProfiles(in String packageName); boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); + void clearInteractAcrossProfilesAppOps(); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 50bee854c027..1e0b2e358e17 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1118,6 +1118,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public @Nullable DataLoaderParams getDataLoaderParams() { try { DataLoaderParamsParcel data = mSession.getDataLoaderParams(); @@ -1157,6 +1158,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes, @NonNull byte[] metadata, @Nullable byte[] signature) { try { @@ -1180,6 +1182,7 @@ public class PackageInstaller { * {@hide} */ @SystemApi + @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2) public void removeFile(@FileLocation int location, @NonNull String name) { try { mSession.removeFile(location, name); @@ -1927,7 +1930,9 @@ public class PackageInstaller { * {@hide} */ @SystemApi - @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) + @RequiresPermission(allOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.USE_INSTALLER_V2}) public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) { this.dataLoaderParams = dataLoaderParams; } diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl index 8bcaf828be97..e7219caf6cd8 100644 --- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl +++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl @@ -26,7 +26,9 @@ package android.hardware.biometrics; oneway interface IBiometricServiceReceiverInternal { // Notify BiometricService that authentication was successful. If user confirmation is required, // the auth token must be submitted into KeyStore. - void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token); + // TODO(b/151967372): Strength should be changed to authenticatorId + void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token, + boolean isStrongBiometric); // Notify BiometricService authentication was rejected. void onAuthenticationFailed(); // Notify BiometricService than an error has occured. Forward to the correct receiver depending diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6daded4ee641..782fff2f69e0 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -60,7 +60,6 @@ import android.text.method.MovementMethod; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; -import android.util.Size; import android.view.Gravity; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -1480,8 +1479,8 @@ public class InputMethodService extends AbstractInputMethodService { */ public int getMaxWidth() { final WindowManager windowManager = getSystemService(WindowManager.class); - final Size windowSize = windowManager.getCurrentWindowMetrics().getSize(); - return windowSize.getWidth(); + final Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds(); + return windowBounds.width(); } /** diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index b7b3c4fc8add..5d2c9d18c00c 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -607,6 +607,9 @@ public class Process { * started. * @param pkgDataInfoMap Map from related package names to private data directory * volume UUID and inode number. + * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * volume UUID and inode number. + * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. * @param zygoteArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. @@ -631,13 +634,17 @@ public class Process { @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, + @Nullable Map<String, Pair<String, Long>> + whitelistedDataInfoMap, + boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] zygoteArgs) { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges, - pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs); + pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData, + bindMountAppStorageDirs, zygoteArgs); } /** @hide */ @@ -661,7 +668,8 @@ public class Process { runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false, - disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs); + disabledCompatChanges, /* pkgDataInfoMap */ null, + /* whitelistedDataInfoMap */ null, false, false, zygoteArgs); } /** diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index d60820ef0f57..8cdcd49cb2cc 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -665,15 +665,17 @@ public class RecoverySystem { * the preparation for unattended update is reset. * * @param context the Context to use. - * @throws IOException if there were any errors setting up unattended update + * @throws IOException if there were any errors clearing the unattended update state * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.RECOVERY) - public static boolean clearPrepareForUnattendedUpdate(@NonNull Context context) + public static void clearPrepareForUnattendedUpdate(@NonNull Context context) throws IOException { RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); - return rs.clearLskf(); + if (!rs.clearLskf()) { + throw new IOException("could not reset unattended update state"); + } } /** @@ -684,21 +686,22 @@ public class RecoverySystem { * @param context the Context to use. * @param updateToken the token used to call {@link #prepareForUnattendedUpdate} before * @param reason the reboot reason to give to the {@link PowerManager} - * @throws IOException if there were any errors setting up unattended update - * @return false if the reboot couldn't proceed because the device wasn't ready for an + * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an * unattended reboot or if the {@code updateToken} did not match the previously * given token * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.RECOVERY) - public static boolean rebootAndApply(@NonNull Context context, @NonNull String updateToken, + public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken, @NonNull String reason) throws IOException { if (updateToken == null) { throw new NullPointerException("updateToken == null"); } RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); - return rs.rebootWithLskf(updateToken, reason); + if (!rs.rebootWithLskf(updateToken, reason)) { + throw new IOException("system not prepared to apply update"); + } } /** diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 5f3f14facd75..a4c99c006d80 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -333,6 +333,9 @@ public class ZygoteProcess { * started. * @param pkgDataInfoMap Map from related package names to private data directory * volume UUID and inode number. + * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * volume UUID and inode number. + * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. * * @param zygoteArgs Additional arguments to supply to the Zygote process. @@ -355,6 +358,9 @@ public class ZygoteProcess { @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, + @Nullable Map<String, Pair<String, Long>> + whitelistedDataInfoMap, + boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] zygoteArgs) { // TODO (chriswailes): Is there a better place to check this value? @@ -367,7 +373,8 @@ public class ZygoteProcess { runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false, packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges, - pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs); + pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData, + bindMountAppStorageDirs, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -608,6 +615,9 @@ public class ZygoteProcess { * @param disabledCompatChanges a list of disabled compat changes for the process being started. * @param pkgDataInfoMap Map from related package names to private data directory volume UUID * and inode number. + * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory + * volume UUID and inode number. + * @param bindMountAppsData whether zygote needs to mount CE and DE data. * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. @@ -631,6 +641,9 @@ public class ZygoteProcess { @Nullable long[] disabledCompatChanges, @Nullable Map<String, Pair<String, Long>> pkgDataInfoMap, + @Nullable Map<String, Pair<String, Long>> + whitelistedDataInfoMap, + boolean bindMountAppsData, boolean bindMountAppStorageDirs, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { @@ -728,11 +741,33 @@ public class ZygoteProcess { } argsForZygote.add(sb.toString()); } + if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append(Zygote.WHITELISTED_DATA_INFO_MAP); + sb.append("="); + boolean started = false; + for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) { + if (started) { + sb.append(','); + } + started = true; + sb.append(entry.getKey()); + sb.append(','); + sb.append(entry.getValue().first); + sb.append(','); + sb.append(entry.getValue().second); + } + argsForZygote.add(sb.toString()); + } if (bindMountAppStorageDirs) { argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS); } + if (bindMountAppsData) { + argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS); + } + if (disabledCompatChanges != null && disabledCompatChanges.length > 0) { StringBuilder sb = new StringBuilder(); sb.append("--disabled-compat-changes="); @@ -1291,6 +1326,7 @@ public class ZygoteProcess { true /* startChildZygote */, null /* packageName */, ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */, null /* disabledCompatChanges */, null /* pkgDataInfoMap */, + null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/, /* bindMountAppStorageDirs */ false, extraArgs); } catch (ZygoteStartFailedEx ex) { diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index a68cc3dfe0e4..aee32ed769ac 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -162,7 +162,12 @@ public class StorageManager { /** {@hide} */ public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.SETTINGS_FUSE_FLAG; - + /** + * Property that determines whether {@link OP_LEGACY_STORAGE} is sticky for + * legacy apps. + * @hide + */ + public static final String PROP_LEGACY_OP_STICKY = "persist.sys.legacy_storage_sticky"; /** {@hide} */ public static final String UUID_PRIVATE_INTERNAL = null; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 60f10cd884ca..bbcb9d9249af 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6564,13 +6564,6 @@ public final class Settings { * Setting specifying if the accessibility shortcut is enabled. * @hide */ - public static final String ACCESSIBILITY_SHORTCUT_ENABLED = - "accessibility_shortcut_enabled"; - - /** - * Setting specifying if the accessibility shortcut is enabled. - * @hide - */ public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN = "accessibility_shortcut_on_lock_screen"; diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java index a9addba375af..b6a8ced29f72 100644 --- a/core/java/android/service/autofill/InlinePresentation.java +++ b/core/java/android/service/autofill/InlinePresentation.java @@ -49,7 +49,8 @@ public final class InlinePresentation implements Parcelable { private final @NonNull InlinePresentationSpec mInlinePresentationSpec; /** - * Indicates whether the UI should be pinned, hence non-scrollable, in the host. + * Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the + * host. */ private final boolean mPinned; diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java index 37a75f0e9e5a..10f526d6565c 100644 --- a/core/java/android/service/controls/actions/ControlAction.java +++ b/core/java/android/service/controls/actions/ControlAction.java @@ -136,7 +136,8 @@ public abstract class ControlAction { /** * Response code for the {@code consumer} in * {@link ControlsProviderService#performControlAction} indicating that in order for the action - * to be performed, acknowledgment from the user is required. + * to be performed, acknowledgment from the user is required. Any non-empty string returned + * from {@link #getChallengeValue} shall be treated as a positive acknowledgment. */ public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3; /** diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index dffcafe1de0e..0ccb1e055c46 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -57,8 +57,8 @@ import java.util.List; * <li>The application display area specifies the part of the display that may contain * an application window, excluding the system decorations. The application display area may * be smaller than the real display area because the system subtracts the space needed - * for decor elements such as the status bar. Use {@link WindowMetrics#getSize()} to query the - * application window size.</li> + * for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the + * application window bounds.</li> * <li>The real display area specifies the part of the display that contains content * including the system decorations. Even so, the real display area may be smaller than the * physical size of the display if the window manager is emulating a smaller display @@ -673,7 +673,7 @@ public final class Display { * * @param outSize A {@link Point} object to receive the size information. * @deprecated Use {@link WindowManager#getCurrentWindowMetrics()} to obtain an instance of - * {@link WindowMetrics} and use {@link WindowMetrics#getSize()} instead. + * {@link WindowMetrics} and use {@link WindowMetrics#getBounds()} instead. */ @Deprecated public void getSize(Point outSize) { @@ -689,7 +689,7 @@ public final class Display { * Gets the size of the display as a rectangle, in pixels. * * @param outSize A {@link Rect} object to receive the size information. - * @deprecated Use {@link WindowMetrics#getSize()} to get the dimensions of the application + * @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application * window area. */ @Deprecated @@ -755,7 +755,7 @@ public final class Display { } /** - * @deprecated Use {@link WindowMetrics#getSize()} instead. + * @deprecated Use {@link WindowMetrics#getBounds#width()} instead. */ @Deprecated public int getWidth() { @@ -766,7 +766,7 @@ public final class Display { } /** - * @deprecated Use {@link WindowMetrics#getSize()} instead. + * @deprecated Use {@link WindowMetrics#getBounds()#height()} instead. */ @Deprecated public int getHeight() { @@ -1105,7 +1105,7 @@ public final class Display { * </p> * * @param outMetrics A {@link DisplayMetrics} object to receive the metrics. - * @deprecated Use {@link WindowMetrics#getSize()} to get the dimensions of the application + * @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application * window area, and {@link Configuration#densityDpi} to get the current density. */ @Deprecated diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index cd22ad6151b8..fe70ff7a1dbf 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -192,6 +192,7 @@ public class SurfaceControlViewHost { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); + lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; setView(view, lp); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d69357bc503d..4922917c911c 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3475,7 +3475,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Flag indicating the field should not have yellow highlight when autofilled. */ - private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x100; + private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x200; /* End of masks for mPrivateFlags4 */ diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 04260c47eda3..4bea623716dc 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -23,11 +23,11 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; import android.os.Build; import android.os.RemoteException; import android.provider.Settings; import android.util.DisplayMetrics; -import android.util.Size; import android.util.SparseArray; import android.util.TypedValue; @@ -410,8 +410,8 @@ public class ViewConfiguration { // Size of the screen in bytes, in ARGB_8888 format final WindowManager windowManager = context.getSystemService(WindowManager.class); - final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize(); - mMaximumDrawingCacheSize = 4 * maxWindowSize.getWidth() * maxWindowSize.getHeight(); + final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds(); + mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height(); mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f); mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f); diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 8bf1ade876ca..316a5f2c88d2 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -35,7 +35,6 @@ import android.graphics.Region; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.util.Size; import com.android.internal.os.IResultReceiver; @@ -220,7 +219,7 @@ public final class WindowManagerImpl implements WindowManager { final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext; final Rect bounds = getCurrentBounds(context); - return new WindowMetrics(toSize(bounds), computeWindowInsets(bounds)); + return new WindowMetrics(bounds, computeWindowInsets(bounds)); } private static Rect getCurrentBounds(Context context) { @@ -232,11 +231,7 @@ public final class WindowManagerImpl implements WindowManager { @Override public WindowMetrics getMaximumWindowMetrics() { final Rect maxBounds = getMaximumBounds(); - return new WindowMetrics(toSize(maxBounds), computeWindowInsets(maxBounds)); - } - - private Size toSize(Rect frame) { - return new Size(frame.width(), frame.height()); + return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds)); } private Rect getMaximumBounds() { diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java index ab5a06eb99de..86ef87997a07 100644 --- a/core/java/android/view/WindowMetrics.java +++ b/core/java/android/view/WindowMetrics.java @@ -18,10 +18,10 @@ package android.view; import android.annotation.NonNull; import android.graphics.Point; -import android.util.Size; +import android.graphics.Rect; /** - * Metrics about a Window, consisting of the size and {@link WindowInsets}. + * Metrics about a Window, consisting of the bounds and {@link WindowInsets}. * <p> * This is usually obtained from {@link WindowManager#getCurrentWindowMetrics()} and * {@link WindowManager#getMaximumWindowMetrics()}. @@ -31,21 +31,22 @@ import android.util.Size; * @see WindowManager#getMaximumWindowMetrics() */ public final class WindowMetrics { - private final @NonNull Size mSize; + private final @NonNull Rect mBounds; private final @NonNull WindowInsets mWindowInsets; - public WindowMetrics(@NonNull Size size, @NonNull WindowInsets windowInsets) { - mSize = size; + public WindowMetrics(@NonNull Rect bounds, @NonNull WindowInsets windowInsets) { + mBounds = bounds; mWindowInsets = windowInsets; } /** - * Returns the size of the window. + * Returns the bounds of the area associated with this window or visual context. * <p> - * <b>Note that this reports a different size than {@link Display#getSize(Point)}.</b> - * This method reports the window size including all system bars area, while - * {@link Display#getSize(Point)} reports the area excluding navigation bars and display cutout - * areas. The value reported by {@link Display#getSize(Point)} can be obtained by using: + * <b>Note that the size of the reported bounds can have different size than + * {@link Display#getSize(Point)}.</b> This method reports the window size including all system + * bar areas, while {@link Display#getSize(Point)} reports the area excluding navigation bars + * and display cutout areas. The value reported by {@link Display#getSize(Point)} can be + * obtained by using: * <pre class="prettyprint"> * final WindowMetrics metrics = windowManager.getCurrentMetrics(); * // Gets all excluding insets @@ -66,16 +67,16 @@ public final class WindowMetrics { * </pre> * </p> * - * @return window size in pixel. + * @return window bounds in pixels. */ - public @NonNull Size getSize() { - return mSize; + public @NonNull Rect getBounds() { + return mBounds; } /** - * Returns the {@link WindowInsets} of the window. + * Returns the {@link WindowInsets} of the area associated with this window or visual context. * - * @return the {@link WindowInsets} of the window. + * @return the {@link WindowInsets} of the visual area. */ public @NonNull WindowInsets getWindowInsets() { return mWindowInsets; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 39a9ed4a82e7..267a5a6561af 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -1242,9 +1242,10 @@ public final class AutofillManager { if (mLastAutofilledData.containsKey(id)) { value = view.getAutofillValue(); valueWasRead = true; + final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1; if (Objects.equals(mLastAutofilledData.get(id), value)) { - view.setAutofilled(true, false); + view.setAutofilled(true, hideHighlight); } else { view.setAutofilled(false, false); mLastAutofilledData.remove(id); diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java index 8d3dc83bca0c..2ead352fd199 100644 --- a/core/java/android/view/autofill/AutofillPopupWindow.java +++ b/core/java/android/view/autofill/AutofillPopupWindow.java @@ -25,7 +25,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.transition.Transition; import android.util.Log; -import android.util.Size; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewTreeObserver; @@ -129,10 +128,10 @@ public class AutofillPopupWindow extends PopupWindow { // Gravity.BOTTOM because PopupWindow base class does not expose computeGravity(). final WindowManager windowManager = anchor.getContext() .getSystemService(WindowManager.class); - final Size windowSize = windowManager.getCurrentWindowMetrics().getSize(); - width = windowSize.getWidth(); + final Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds(); + width = windowBounds.width(); if (height != LayoutParams.MATCH_PARENT) { - offsetY = windowSize.getHeight() - height; + offsetY = windowBounds.height() - height; } actualAnchor = anchor; } else if (virtualBounds != null) { diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 54ea57a6cae4..d64b5f1118dc 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -144,9 +144,6 @@ public class AccessibilityShortcutController { Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE), false, co, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED), - false, co, UserHandle.USER_ALL); - mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN), false, co, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver( @@ -174,8 +171,6 @@ public class AccessibilityShortcutController { public void onSettingsChanged() { final boolean hasShortcutTarget = hasShortcutTarget(); final ContentResolver cr = mContext.getContentResolver(); - final boolean enabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, mUserId) == 1; // Enable the shortcut from the lockscreen by default if the dialog has been shown final int dialogAlreadyShown = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStaus.NOT_SHOWN, @@ -183,7 +178,7 @@ public class AccessibilityShortcutController { mEnabledOnLockScreen = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, dialogAlreadyShown, mUserId) == 1; - mIsShortcutEnabled = enabled && hasShortcutTarget; + mIsShortcutEnabled = hasShortcutTarget; } /** diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index ae9ce655ff3a..c53516389cd9 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -387,9 +387,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { resetViewVisibilitiesForWorkProfileEmptyState(emptyStateView); emptyStateView.setVisibility(View.VISIBLE); - ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon); - icon.setImageResource(iconRes); - TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title); title.setText(titleRes); @@ -401,9 +398,17 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { subtitle.setVisibility(View.GONE); } + ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon); Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button); - button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE); - button.setOnClickListener(buttonOnClick); + if (!getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) { + icon.setVisibility(View.VISIBLE); + icon.setImageResource(iconRes); + button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE); + button.setOnClickListener(buttonOnClick); + } else { + icon.setVisibility(View.GONE); + button.setVisibility(View.GONE); + } activeListAdapter.markTabLoaded(); } diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java index c40864131a2e..51b13345a6c3 100644 --- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java +++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java @@ -169,13 +169,41 @@ public class AccessibilityButtonChooserActivity extends Activity { private static List<AccessibilityButtonTarget> getInstalledServiceTargets( @NonNull Context context) { final List<AccessibilityButtonTarget> targets = new ArrayList<>(); - targets.addAll(getAccessibilityServiceTargets(context)); - targets.addAll(getAccessibilityActivityTargets(context)); + targets.addAll(getAccessibilityFilteredTargets(context)); targets.addAll(getWhiteListingServiceTargets(context)); return targets; } + private static List<AccessibilityButtonTarget> getAccessibilityFilteredTargets( + @NonNull Context context) { + final List<AccessibilityButtonTarget> serviceTargets = + getAccessibilityServiceTargets(context); + final List<AccessibilityButtonTarget> activityTargets = + getAccessibilityActivityTargets(context); + + for (AccessibilityButtonTarget activityTarget : activityTargets) { + serviceTargets.removeIf(serviceTarget -> { + final ComponentName serviceComponentName = + ComponentName.unflattenFromString(serviceTarget.getId()); + final ComponentName activityComponentName = + ComponentName.unflattenFromString(activityTarget.getId()); + final boolean isSamePackageName = activityComponentName.getPackageName().equals( + serviceComponentName.getPackageName()); + final boolean isSameLabel = activityTarget.getLabel().equals( + serviceTarget.getLabel()); + + return isSamePackageName && isSameLabel; + }); + } + + final List<AccessibilityButtonTarget> targets = new ArrayList<>(); + targets.addAll(serviceTargets); + targets.addAll(activityTargets); + + return targets; + } + private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets( @NonNull Context context) { final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index a1a434d3bc64..dca682e4ee29 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2674,7 +2674,7 @@ public class ChooserActivity extends ResolverActivity implements */ private boolean shouldShowStickyContentPreview() { return shouldShowStickyContentPreviewNoOrientationCheck() - && getResources().getBoolean(R.bool.sharesheet_show_content_preview); + && !getResources().getBoolean(R.bool.resolver_landscape_phone); } private boolean shouldShowStickyContentPreviewNoOrientationCheck() { diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index ff03f1a1a2ab..c75898994f3e 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -205,9 +205,15 @@ public final class Zygote { /** List of packages with the same uid, and its app data info: volume uuid and inode. */ public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map"; + /** List of whitelisted packages and its app data info: volume uuid and inode. */ + public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map"; + /** Bind mount app storage dirs to lower fs not via fuse */ public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs"; + /** Bind mount app storage dirs to lower fs not via fuse */ + public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs"; + /** * An extraArg passed when a zygote process is forking a child-zygote, specifying a name * in the abstract socket namespace. This socket name is what the new child zygote @@ -313,6 +319,8 @@ public final class Zygote { * @param isTopApp true if the process is for top (high priority) application. * @param pkgDataInfoList A list that stores related packages and its app data * info: volume uuid and inode. + * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps. + * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. * * @return 0 if this is the child, pid of the child @@ -321,13 +329,15 @@ public final class Zygote { static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, - boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) { + boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList, + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) { ZygoteHooks.preFork(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp, - pkgDataInfoList, bindMountAppStorageDirs); + pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs, + bindMountAppStorageDirs); if (pid == 0) { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -344,6 +354,7 @@ public final class Zygote { int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, String[] pkgDataInfoList, + String[] whitelistedDataInfoList, boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs); /** @@ -371,15 +382,19 @@ public final class Zygote { * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name, * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid, * app_b_ce_inode, ...]; + * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps. + * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. */ private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, - String[] pkgDataInfoList, boolean bindMountAppStorageDirs) { + String[] pkgDataInfoList, String[] whitelistedDataInfoList, + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) { nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, isTopApp, - pkgDataInfoList, bindMountAppStorageDirs); + pkgDataInfoList, whitelistedDataInfoList, + bindMountAppDataDirs, bindMountAppStorageDirs); // Note that this event ends at the end of handleChildProc. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -399,7 +414,8 @@ public final class Zygote { private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, - String[] pkgDataInfoList, boolean bindMountAppStorageDirs); + String[] pkgDataInfoList, String[] whitelistedDataInfoList, + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs); /** * Called to do any initialization before starting an application. @@ -724,7 +740,8 @@ public final class Zygote { args.mRuntimeFlags, rlimits, args.mMountExternal, args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir, args.mIsTopApp, - args.mPkgDataInfoList, args.mBindMountAppStorageDirs); + args.mPkgDataInfoList, args.mWhitelistedDataInfoList, + args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index 1a63765fcaa6..94c1f71a26db 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -227,11 +227,22 @@ class ZygoteArguments { String[] mPkgDataInfoList; /** + * A list that stores all whitelisted app data info: volume uuid and inode. + * Null if it does need to do app data isolation. + */ + String[] mWhitelistedDataInfoList; + + /** * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS */ boolean mBindMountAppStorageDirs; /** + * @see Zygote#BIND_MOUNT_APP_DATA_DIRS + */ + boolean mBindMountAppDataDirs; + + /** * Constructs instance and parses args * * @param args zygote command-line args @@ -452,8 +463,12 @@ class ZygoteArguments { } } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) { mPkgDataInfoList = getAssignmentList(arg); + } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) { + mWhitelistedDataInfoList = getAssignmentList(arg); } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) { mBindMountAppStorageDirs = true; + } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { + mBindMountAppDataDirs = true; } else { break; } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index bc8dfd4aa402..6e880d43b73e 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -258,7 +258,8 @@ class ZygoteConnection { parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp, - parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs); + parsedArgs.mPkgDataInfoList,parsedArgs.mWhitelistedDataInfoList, + parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs); try { if (pid == 0) { diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 0aeaa47ba3d8..980943ebea5a 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -22,14 +22,10 @@ import android.annotation.Nullable; import android.annotation.StyleRes; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; -import android.util.Size; -import android.view.Display; import android.view.Gravity; import android.view.View; import android.view.WindowManager; -import android.view.WindowMetrics; import android.widget.PopupWindow.OnDismissListener; import com.android.internal.view.menu.MenuPresenter.Callback; @@ -227,9 +223,9 @@ public class MenuPopupHelper implements MenuHelper { @NonNull private MenuPopup createPopup() { final WindowManager windowManager = mContext.getSystemService(WindowManager.class); - final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize(); + final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds(); - final int smallestWidth = Math.min(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + final int smallestWidth = Math.min(maxWindowBounds.width(), maxWindowBounds.height()); final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.cascading_menus_min_smallest_width); final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading; diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp index 17a02b24c697..34be2a52344d 100644 --- a/core/jni/android_media_AudioProductStrategies.cpp +++ b/core/jni/android_media_AudioProductStrategies.cpp @@ -85,10 +85,23 @@ static jint convertAudioProductStrategiesFromNative( jStrategyId = static_cast<jint>(strategy.getId()); // Audio Attributes Group array - std::map<int, std::vector<AudioAttributes> > groups; + int attrGroupIndex = 0; + std::map<int /**attributesGroupIndex*/, std::vector<AudioAttributes> > groups; for (const auto &attr : strategy.getAudioAttributes()) { - int attrGroupId = attr.getGroupId(); - groups[attrGroupId].push_back(attr); + int groupId = attr.getGroupId(); + int streamType = attr.getStreamType(); + const auto &iter = std::find_if(begin(groups), end(groups), + [groupId, streamType](const auto &iter) { + const auto &frontAttr = iter.second.front(); + return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType; + }); + // Same Volume Group Id and same stream type + if (iter != end(groups)) { + groups[iter->first].push_back(attr); + } else { + // Add a new Group of AudioAttributes for this product strategy + groups[attrGroupIndex++].push_back(attr); + } } numAttributesGroups = groups.size(); @@ -97,7 +110,7 @@ static jint convertAudioProductStrategiesFromNative( for (const auto &iter : groups) { std::vector<AudioAttributes> audioAttributesGroups = iter.second; jint numAttributes = audioAttributesGroups.size(); - jint jGroupId = iter.first; + jint jGroupId = audioAttributesGroups.front().getGroupId(); jint jLegacyStreamType = audioAttributesGroups.front().getStreamType(); jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index ea3c0fa9fc3c..aa2d1b5fa02b 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -110,7 +110,6 @@ using android::base::StringAppendF; using android::base::StringPrintf; using android::base::WriteStringToFile; using android::base::GetBoolProperty; -using android::base::GetProperty; #define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \ append(StringPrintf(__VA_ARGS__)) @@ -170,18 +169,6 @@ static int gSystemServerSocketFd = -1; static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751; -/** - * Property to control if app data isolation is enabled. - */ -static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY = - "persist.zygote.app_data_isolation"; - -/** - * Property to enable app data isolation for sdcard obb or data in vold. - */ -static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = - "persist.sys.vold_app_data_isolation_enabled"; - static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000; static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF; @@ -1319,20 +1306,13 @@ static void relabelAllDirs(const char* path, security_context_t context, fail_fn * be decrypted after storage is decrypted. * */ -static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, - uid_t uid, const char* process_name, jstring managed_nice_name, - fail_fn_t fail_fn) { +static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list, + uid_t uid, const char* process_name, + jstring managed_nice_name, fail_fn_t fail_fn) { const userid_t userId = multiuser_get_user_id(uid); - auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); - - int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0; - // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode> - if ((size % 3) != 0) { - fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size)); - } - ensureInAppMountNamespace(fail_fn); + int size = merged_data_info_list.size(); // Mount tmpfs on all possible data directories, so app no longer see the original apps data. char internalCePath[PATH_MAX]; @@ -1377,14 +1357,10 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, bool legacySymlinkCreated = false; for (int i = 0; i < size; i += 3) { - jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i)); - std::string packageName = extract_fn(package_str).value(); + std::string const & packageName = merged_data_info_list[i]; + std::string const & volUuid = merged_data_info_list[i + 1]; + std::string const & inode = merged_data_info_list[i + 2]; - jstring vol_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 1)); - std::string volUuid = extract_fn(vol_str).value(); - - jstring inode_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 2)); - std::string inode = extract_fn(inode_str).value(); std::string::size_type sz; long long ceDataInode = std::stoll(inode, &sz); @@ -1482,6 +1458,48 @@ static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, freecon(dataDataContext); } +static void insertPackagesToMergedList(JNIEnv* env, + std::vector<std::string>& merged_data_info_list, + jobjectArray data_info_list, const char* process_name, + jstring managed_nice_name, fail_fn_t fail_fn) { + + auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); + + int size = (data_info_list != nullptr) ? env->GetArrayLength(data_info_list) : 0; + // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode> + if ((size % 3) != 0) { + fail_fn(CREATE_ERROR("Wrong data_info_list size %d", size)); + } + + for (int i = 0; i < size; i += 3) { + jstring package_str = (jstring) (env->GetObjectArrayElement(data_info_list, i)); + std::string packageName = extract_fn(package_str).value(); + merged_data_info_list.push_back(packageName); + + jstring vol_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 1)); + std::string volUuid = extract_fn(vol_str).value(); + merged_data_info_list.push_back(volUuid); + + jstring inode_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 2)); + std::string inode = extract_fn(inode_str).value(); + merged_data_info_list.push_back(inode); + } +} + +static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list, + jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name, + jstring managed_nice_name, fail_fn_t fail_fn) { + + ensureInAppMountNamespace(fail_fn); + std::vector<std::string> merged_data_info_list; + insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list, + process_name, managed_nice_name, fail_fn); + insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list, + process_name, managed_nice_name, fail_fn); + + isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn); +} + /** * Like isolateAppData(), isolate jit profile directories, so apps don't see what * other apps are installed by reading content inside /data/misc/profiles/cur. @@ -1594,7 +1612,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jstring managed_nice_name, bool is_system_server, bool is_child_zygote, jstring managed_instruction_set, jstring managed_app_data_dir, bool is_top_app, - jobjectArray pkg_data_info_list, bool mount_storage_dirs) { + jobjectArray pkg_data_info_list, + jobjectArray whitelisted_data_info_list, + bool mount_data_dirs, bool mount_storage_dirs) { const char* process_name = is_system_server ? "system_server" : "zygote"; auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1); auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); @@ -1628,9 +1648,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // give a null in same_uid_pkgs and private_volumes so they don't need app data isolation. // Isolated process / webview / app zygote should be gated by SELinux and file permission // so they can't even traverse CE / DE directories. - if (pkg_data_info_list != nullptr - && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) { - isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); + if (mount_data_dirs) { + isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list, + uid, process_name, managed_nice_name, fail_fn); isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn); } if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) { @@ -2003,7 +2023,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( jint mount_external, jstring se_info, jstring nice_name, jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, - jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) { + jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, + jboolean mount_data_dirs, jboolean mount_storage_dirs) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); if (UNLIKELY(managed_fds_to_close == nullptr)) { @@ -2041,6 +2062,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list, + whitelisted_data_info_list, + mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE); } return pid; @@ -2076,7 +2099,8 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( permitted_capabilities, effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true, false, nullptr, nullptr, /* is_top_app= */ false, - /* pkg_data_info_list */ nullptr, false); + /* pkg_data_info_list */ nullptr, + /* whitelisted_data_info_list */ nullptr, false, false); } else if (pid > 0) { // The zygote process checks whether the child process has died or not. ALOGI("System server process %d has been created", pid); @@ -2206,15 +2230,16 @@ static void com_android_internal_os_Zygote_nativeSpecializeAppProcess( jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, - jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) { + jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list, + jboolean mount_data_dirs, jboolean mount_storage_dirs) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities, mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, - is_top_app == JNI_TRUE, pkg_data_info_list, - mount_storage_dirs == JNI_TRUE); + is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list, + mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE); } /** @@ -2408,7 +2433,7 @@ static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas static const JNINativeMethod gMethods[] = { {"nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/" - "String;Z[Ljava/lang/String;Z)I", + "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I", (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize}, {"nativeForkSystemServer", "(II[II[[IJJ)I", (void*)com_android_internal_os_Zygote_nativeForkSystemServer}, @@ -2421,7 +2446,7 @@ static const JNINativeMethod gMethods[] = { {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap}, {"nativeSpecializeAppProcess", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/" - "String;Z[Ljava/lang/String;Z)V", + "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V", (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess}, {"nativeInitNativeState", "(Z)V", (void*)com_android_internal_os_Zygote_nativeInitNativeState}, diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto index 42437d5b44a0..563ef145b79c 100644 --- a/core/proto/android/app/enums.proto +++ b/core/proto/android/app/enums.proto @@ -203,9 +203,7 @@ enum AppOpEnum { APP_OP_INTERACT_ACROSS_PROFILES = 93; APP_OP_ACTIVATE_PLATFORM_VPN = 94; APP_OP_LOADER_USAGE_STATS = 95; - APP_OP_ACCESS_CALL_AUDIO = 96; + APP_OP_DEPRECATED_1 = 96 [deprecated = true]; APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97; APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98; } - - diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 84d9ce288069..eae614546dfb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1213,15 +1213,6 @@ android:description="@string/permdesc_acceptHandovers" android:protectionLevel="dangerous" /> - <!-- Allows an application assigned to the Dialer role to be granted access to the telephony - call audio streams, both TX and RX. - <p>Protection level: signature|appop - --> - <permission android:name="android.permission.ACCESS_CALL_AUDIO" - android.label="@string/permlab_accessCallAudio" - android:description="@string/permdesc_accessCallAudio" - android:protectionLevel="signature|appop" /> - <!-- ====================================================================== --> <!-- Permissions for accessing the device microphone --> <!-- ====================================================================== --> @@ -3654,6 +3645,16 @@ <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to use the package installer v2 APIs. + <p>The package installer v2 APIs are still a work in progress and we're + currently validating they work in all scenarios. + <p>Not for use by third-party applications. + TODO(b/152310230): remove this permission once the APIs are confirmed to be sufficient. + @hide + --> + <permission android:name="com.android.permission.USE_INSTALLER_V2" + android:protectionLevel="signature|verifier" /> + <!-- @SystemApi @TestApi Allows an application to clear user data. <p>Not for use by third-party applications @hide diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml index 210feaf092a7..c7e1a21aa642 100644 --- a/core/res/res/layout/resolver_empty_states.xml +++ b/core/res/res/layout/resolver_empty_states.xml @@ -22,11 +22,11 @@ android:orientation="vertical" android:gravity="center_horizontal" android:visibility="gone" + android:paddingTop="48dp" android:paddingStart="24dp" android:paddingEnd="24dp"> <ImageView android:id="@+id/resolver_empty_state_icon" - android:layout_marginTop="48dp" android:layout_width="24dp" android:layout_height="24dp" android:layout_centerHorizontal="true" /> diff --git a/core/res/res/values-h480dp/bools.xml b/core/res/res/values-h480dp/bools.xml index 65e3ae6e71ec..7896d9bfe2b1 100644 --- a/core/res/res/values-h480dp/bools.xml +++ b/core/res/res/values-h480dp/bools.xml @@ -16,5 +16,5 @@ --> <resources> - <bool name="sharesheet_show_content_preview">true</bool> + <bool name="resolver_landscape_phone">false</bool> </resources>
\ No newline at end of file diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 28eb98b07690..2a41542d38e4 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1827,6 +1827,9 @@ <attr name="gwpAsanMode" /> + <!-- @hide no longer used, kept to preserve padding --> + <attr name="allowAutoRevokePermissionsExemption" format="boolean" /> + <attr name="autoRevokePermissions"> <enum name="allowed" value="0" /> <enum name="discouraged" value="1" /> diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml index c5127dccdae7..fe296c704095 100644 --- a/core/res/res/values/bools.xml +++ b/core/res/res/values/bools.xml @@ -29,5 +29,5 @@ <p>The main purpose is for OEMs to customize the rendering of the lockscreen, setting this to true should come with customized drawables. --> <bool name="use_lock_pattern_drawable">false</bool> - <bool name="sharesheet_show_content_preview">false</bool> + <bool name="resolver_landscape_phone">true</bool> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e694e160009e..738688b36257 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3014,6 +3014,8 @@ <!-- @hide @SystemApi --> <public name="minExtensionVersion" /> <public name="allowNativeHeapPointerTagging" /> + <!-- @hide no longer used, kept to preserve padding --> + <public name="allowAutoRevokePermissionsExemption"/> <public name="autoRevokePermissions" /> <public name="preserveLegacyExternalStorage" /> <public name="mimeGroup" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 06f776013233..5b880365657c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5482,11 +5482,6 @@ <!-- Error message. This text lets the user know that their current personal apps can't open this specific content. [CHAR LIMIT=NONE] --> <string name="resolver_no_personal_apps_available_resolve">No personal apps can open this content</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> - <string name="permlab_accessCallAudio">Record or play audio in telephony calls</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> - <string name="permdesc_accessCallAudio">Allows this app, when assigned as default dialer application, to record or play audio in telephony calls.</string> - <!-- Icc depersonalization related strings --> <!-- Label text for PIN entry widget on SIM Network Depersonalization panel [CHAR LIMIT=none] --> <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY">SIM network unlock PIN</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0adef7513bb5..22caf4c2a37d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3929,7 +3929,7 @@ <java-symbol type="dimen" name="resolver_empty_state_height" /> <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" /> <java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" /> - <java-symbol type="bool" name="sharesheet_show_content_preview" /> + <java-symbol type="bool" name="resolver_landscape_phone" /> <java-symbol type="dimen" name="resolver_tab_text_size" /> <!-- Toast message for background started foreground service while-in-use permission restriction feature --> diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java index 79b803a0cda6..0cd0643ee8c0 100644 --- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java +++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java @@ -25,13 +25,13 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.Color; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.util.Log; -import android.util.Size; import android.view.Display; import android.view.Gravity; import android.view.View; @@ -119,15 +119,15 @@ public class TestService extends Service { @Override public void showApplicationOverlay() throws RemoteException { final WindowManager wm = mOverlayContext.getSystemService(WindowManager.class); - final Size size = wm.getCurrentWindowMetrics().getSize(); + final Rect bounds = wm.getCurrentWindowMetrics().getBounds(); final WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams( TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); - wmlp.width = size.getWidth() / 2; - wmlp.height = size.getHeight() / 2; + wmlp.width = bounds.width() / 2; + wmlp.height = bounds.height() / 2; wmlp.gravity = Gravity.CENTER | Gravity.LEFT; wmlp.setTitle(TAG); diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java index 57e5dd8f8f20..f48e66681cc7 100644 --- a/core/tests/coretests/src/android/content/ContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ContentResolverTest.java @@ -238,12 +238,9 @@ public class ContentResolverTest { @Test public void testGetType_providerException() { - try { - mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error")); - fail("Expected IllegalArgumentException"); - } catch (IllegalArgumentException e) { - // Expected - } + String type = + mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error")); + assertThat(type).isNull(); } @Test @@ -253,4 +250,15 @@ public class ContentResolverTest { assertThat(canonical).isEqualTo( Uri.parse("content://android.content.FakeProviderRemote/canonical")); } + + @Test + public void testCanonicalize_providerException() { + try { + mResolver.canonicalize( + Uri.parse("content://android.content.FakeProviderRemote/error")); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + } } diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java index a32009493094..8bc56607f555 100644 --- a/core/tests/coretests/src/android/content/FakeProviderRemote.java +++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java @@ -60,6 +60,9 @@ public class FakeProviderRemote extends ContentProvider { @Override public Uri canonicalize(Uri uri) { + if (uri.getPath() != null && uri.getPath().contains("error")) { + throw new IllegalArgumentException("Expected exception"); + } return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority()) .appendPath("canonical").build(); } diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java index df5c9d2df90b..4114b28a7252 100644 --- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java +++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java @@ -184,7 +184,7 @@ public class ResourcesManagerTest extends TestCase { @SmallTest public void testThemesGetUpdatedWithNewImpl() { Binder activity1 = new Binder(); - Resources resources1 = mResourcesManager.createBaseActivityResources( + Resources resources1 = mResourcesManager.createBaseTokenResources( activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); @@ -217,7 +217,7 @@ public class ResourcesManagerTest extends TestCase { // Create a Resources for the Activity. Configuration config1 = new Configuration(); config1.densityDpi = 280; - Resources resources1 = mResourcesManager.createBaseActivityResources( + Resources resources1 = mResourcesManager.createBaseTokenResources( activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null); assertNotNull(resources1); diff --git a/core/tests/coretests/src/android/util/GridScenario.java b/core/tests/coretests/src/android/util/GridScenario.java index 4809a213ab48..e7ee1cd59c7c 100644 --- a/core/tests/coretests/src/android/util/GridScenario.java +++ b/core/tests/coretests/src/android/util/GridScenario.java @@ -233,7 +233,7 @@ public abstract class GridScenario extends Activity { // turn off title bar requestWindowFeature(Window.FEATURE_NO_TITLE); - mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height(); final Params params = new Params(); init(params); diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/util/ListScenario.java index d4e5a438d855..74dc4b4b34a1 100644 --- a/core/tests/coretests/src/android/util/ListScenario.java +++ b/core/tests/coretests/src/android/util/ListScenario.java @@ -306,7 +306,7 @@ public abstract class ListScenario extends Activity { requestWindowFeature(Window.FEATURE_NO_TITLE); - mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height(); final Params params = createParams(); init(params); diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/util/ScrollViewScenario.java index 2c0aa7362dfa..ab1a642a9327 100644 --- a/core/tests/coretests/src/android/util/ScrollViewScenario.java +++ b/core/tests/coretests/src/android/util/ScrollViewScenario.java @@ -239,7 +239,7 @@ public abstract class ScrollViewScenario extends Activity { // for test stability, turn off title bar requestWindowFeature(Window.FEATURE_NO_TITLE); - int screenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight() + int screenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height() - 25; mLinearLayout = new LinearLayout(this); mLinearLayout.setOrientation(LinearLayout.VERTICAL); diff --git a/core/tests/coretests/src/android/view/BigCache.java b/core/tests/coretests/src/android/view/BigCache.java index e465a859218a..3038e79abd72 100644 --- a/core/tests/coretests/src/android/view/BigCache.java +++ b/core/tests/coretests/src/android/view/BigCache.java @@ -17,8 +17,8 @@ package android.view; import android.app.Activity; +import android.graphics.Rect; import android.os.Bundle; -import android.util.Size; import android.widget.LinearLayout; import android.widget.ScrollView; @@ -39,9 +39,9 @@ public class BigCache extends Activity { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); final int cacheSize = ViewConfiguration.getMaximumDrawingCacheSize(); - final Size windowSize = getWindowManager().getCurrentWindowMetrics().getSize(); - final int screenWidth = windowSize.getWidth(); - final int screenHeight = windowSize.getHeight(); + final Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds(); + final int screenWidth = windowBounds.width(); + final int screenHeight = windowBounds.height(); final View tiny = new View(this); tiny.setId(R.id.a); diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java index 039387c85b11..46e55faae8b6 100644 --- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java +++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java @@ -22,7 +22,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import android.util.Size; +import android.graphics.Rect; import android.widget.TextView; import androidx.test.filters.LargeTest; @@ -55,9 +55,9 @@ public class ScaleGestureDetectorTest { // Specify start and end coordinates with respect to the window size. final WindowManager wm = mScaleGestureActivity.getSystemService(WindowManager.class); - final Size windowSize = wm.getCurrentWindowMetrics().getSize(); - final int windowWidth = windowSize.getWidth(); - final int windowHeight = windowSize.getHeight(); + final Rect windowBounds = wm.getCurrentWindowMetrics().getBounds(); + final int windowWidth = windowBounds.width(); + final int windowHeight = windowBounds.height(); // Obtain coordinates to perform pinch and zoom from the center, to 75% of the display. final int centerX = windowWidth / 2; diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java index fa6886075bfd..74524bf6d76f 100644 --- a/core/tests/coretests/src/android/view/WindowMetricsTest.java +++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java @@ -22,10 +22,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static org.junit.Assert.assertTrue; import android.content.Context; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.platform.test.annotations.Presubmit; -import android.util.Size; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; @@ -87,10 +87,12 @@ public class WindowMetricsTest { private static void verifyMetricsSanity(WindowMetrics currentMetrics, WindowMetrics maxMetrics) { - Size currentSize = currentMetrics.getSize(); - Size maxSize = maxMetrics.getSize(); + Rect currentBounds = currentMetrics.getBounds(); + Rect maxBounds = maxMetrics.getBounds(); - assertTrue(maxSize.getWidth() >= currentSize.getWidth()); - assertTrue(maxSize.getHeight() >= currentSize.getHeight()); + assertTrue(maxBounds.width() >= currentBounds.width()); + assertTrue(maxBounds.height() >= currentBounds.height()); + assertTrue(maxBounds.left >= 0); + assertTrue(maxBounds.top >= 0); } } diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java index d5825e20163c..4bd9ccd8d4d3 100644 --- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java +++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java @@ -16,9 +16,9 @@ package android.view.menu; +import android.graphics.Rect; import android.test.ActivityInstrumentationTestCase; import android.util.PollingCheck; -import android.util.Size; import android.view.View; import android.view.WindowManager; import android.widget.espresso.ContextMenuUtils; @@ -81,8 +81,8 @@ public class ContextMenuTest extends ActivityInstrumentationTestCase<ContextMenu */ private int getMinScreenDimension() { final WindowManager windowManager = getActivity().getSystemService(WindowManager.class); - final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize(); - return Math.min(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds(); + return Math.min(maxWindowBounds.width(), maxWindowBounds.height()); } /** diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java index 1b5ce8fd4ce5..9a93dbf67d33 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java @@ -37,6 +37,7 @@ import static org.junit.Assert.assertTrue; import android.app.Activity; import android.app.Instrumentation; import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; import android.text.Layout; import android.text.Spannable; import android.text.SpannableString; @@ -48,7 +49,7 @@ import android.view.MotionEvent; import android.view.View; import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; +import androidx.test.filters.MediumTest; import androidx.test.filters.Suppress; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; @@ -67,7 +68,8 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @RunWith(AndroidJUnit4.class) -@SmallTest +@MediumTest +@Presubmit public class EditorCursorDragTest { private static final String LOG_TAG = EditorCursorDragTest.class.getSimpleName(); diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java index 3dc001d68a02..ec75e40f1334 100644 --- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java +++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertTrue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import android.platform.test.annotations.Presubmit; import android.view.InputDevice; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -36,6 +37,7 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) @SmallTest +@Presubmit public class EditorTouchStateTest { private EditorTouchState mTouchState; diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java index 8e90a824c873..d51cc328e4fb 100644 --- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java +++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java @@ -111,7 +111,7 @@ public class ListOfInternalSelectionViews extends Activity { protected void onCreate(Bundle icicle) { super.onCreate(icicle); - mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height(); Bundle extras = getIntent().getExtras(); if (extras != null) { diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java index fd1dbfc63708..5cedd13533e7 100644 --- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java +++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java @@ -106,8 +106,8 @@ public class GridTouchVerticalSpacingStackFromBottomTest extends ActivityInstrum int firstTop = firstChild.getTop(); - int windowHeight = mActivity.getWindowManager().getCurrentWindowMetrics().getSize() - .getHeight(); + int windowHeight = mActivity.getWindowManager().getCurrentWindowMetrics().getBounds() + .height(); int distance = TouchUtils.dragViewBy(this, firstChild, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, (int) (windowHeight * 0.75f)); diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java index e6195b147045..5cca766e1b1e 100644 --- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java +++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java @@ -66,7 +66,7 @@ public class AdjacentListsWithAdjacentISVsInside extends Activity { super.onCreate(savedInstanceState); final int desiredHeight = - (int) (0.8 * getWindowManager().getCurrentWindowMetrics().getSize().getHeight()); + (int) (0.8 * getWindowManager().getCurrentWindowMetrics().getBounds().height()); mLeftListView = new ListView(this); mLeftListView.setAdapter(new AdjacentISVAdapter(desiredHeight)); diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index 9af0ed0ab826..4a33da680585 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -17,7 +17,6 @@ package com.android.internal.accessibility; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN; -import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY; @@ -102,10 +101,8 @@ public class AccessibilityShortcutControllerTest { private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2}; // Convenience values for enabling/disabling to make code more readable - private static final int DISABLED = 0; private static final int ENABLED_EXCEPT_LOCK_SCREEN = 1; private static final int ENABLED_INCLUDING_LOCK_SCREEN = 2; - private static final int DISABLED_BUT_LOCK_SCREEN_ON = 3; private @Mock Context mContext; private @Mock FrameworkObjectProvider mFrameworkObjectProvider; @@ -225,14 +222,6 @@ public class AccessibilityShortcutControllerTest { } @Test - public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse() - throws Exception { - configureValidShortcutService(); - configureShortcutEnabled(DISABLED_BUT_LOCK_SCREEN_ON); - assertFalse(getController().isAccessibilityShortcutAvailable(false)); - } - - @Test public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse() throws Exception { configureValidShortcutService(); @@ -285,20 +274,8 @@ public class AccessibilityShortcutControllerTest { } @Test - public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse() - throws Exception { - configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); - configureValidShortcutService(); - AccessibilityShortcutController accessibilityShortcutController = getController(); - configureShortcutEnabled(DISABLED); - accessibilityShortcutController.onSettingsChanged(); - assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); - } - - @Test public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue() throws Exception { - configureShortcutEnabled(DISABLED); configureValidShortcutService(); AccessibilityShortcutController accessibilityShortcutController = getController(); configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); @@ -594,31 +571,19 @@ public class AccessibilityShortcutControllerTest { } private void configureShortcutEnabled(int enabledValue) { - final boolean enabled; final boolean lockscreen; switch (enabledValue) { - case DISABLED: - enabled = false; - lockscreen = false; - break; - case DISABLED_BUT_LOCK_SCREEN_ON: - enabled = false; - lockscreen = true; - break; case ENABLED_INCLUDING_LOCK_SCREEN: - enabled = true; lockscreen = true; break; case ENABLED_EXCEPT_LOCK_SCREEN: - enabled = true; lockscreen = false; break; default: throw new IllegalArgumentException(); } - Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_ENABLED, enabled ? 1 : 0); Settings.Secure.putInt( mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, lockscreen ? 1 : 0); } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 8b973a177f90..0a56accd1636 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -708,6 +708,8 @@ public class AudioSystem DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET); } + public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0"; + // device states, must match AudioSystem::device_connection_state @UnsupportedAppUsage public static final int DEVICE_STATE_UNAVAILABLE = 0; diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java index f9dbc50e20cf..090f78e4e4f7 100644 --- a/media/java/android/media/audiopolicy/AudioProductStrategy.java +++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java @@ -374,8 +374,8 @@ public final class AudioProductStrategy implements Parcelable { if (refAttr.equals(sDefaultAttributes)) { return false; } - return ((refAttr.getUsage() == AudioAttributes.USAGE_UNKNOWN) - || (attr.getUsage() == refAttr.getUsage())) + return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN) + || (attr.getSystemUsage() == refAttr.getSystemUsage())) && ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN) || (attr.getContentType() == refAttr.getContentType())) && ((refAttr.getAllFlags() == 0) diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 29dfd730a84c..9310d38c3bd1 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -40,6 +40,7 @@ using ::android::hardware::tv::tuner::V1_0::DataFormat; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings; using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType; using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType; +using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities; using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent; using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings; @@ -1382,6 +1383,42 @@ jobject JTuner::openDvr(DvrType type, jlong bufferSize) { return dvrObj; } +jobject JTuner::getDemuxCaps() { + DemuxCapabilities caps; + Result res; + mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) { + caps = demuxCaps; + res = r; + }); + if (res != Result::SUCCESS) { + return NULL; + } + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V"); + + jint numDemux = caps.numDemux; + jint numRecord = caps.numRecord; + jint numPlayback = caps.numPlayback; + jint numTsFilter = caps.numTsFilter; + jint numSectionFilter = caps.numSectionFilter; + jint numAudioFilter = caps.numAudioFilter; + jint numVideoFilter = caps.numVideoFilter; + jint numPesFilter = caps.numPesFilter; + jint numPcrFilter = caps.numPcrFilter; + jlong numBytesInSectionFilter = caps.numBytesInSectionFilter; + jint filterCaps = static_cast<jint>(caps.filterCaps); + jboolean bTimeFilter = caps.bTimeFilter; + + jintArray linkCaps = env->NewIntArray(caps.linkCaps.size()); + env->SetIntArrayRegion( + linkCaps, 0, caps.linkCaps.size(), reinterpret_cast<jint*>(&caps.linkCaps[0])); + + return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter, + numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter, + numBytesInSectionFilter, filterCaps, linkCaps, bTimeFilter); +} + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -2739,8 +2776,9 @@ static jobject android_media_tv_Tuner_open_dvr_playback( return tuner->openDvr(DvrType::PLAYBACK, bufferSize); } -static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) { - return NULL; +static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->getDemuxCaps(); } static int android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 5d2bba6c8aa2..18aac285e7c0 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -185,6 +185,7 @@ struct JTuner : public RefBase { jobject openTimeFilter(); jobject openDescrambler(); jobject openDvr(DvrType type, jlong bufferSize); + jobject getDemuxCaps(); protected: Result openDemux(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java index 25220951dd7b..daeb731ad457 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java @@ -2210,14 +2210,14 @@ public class CameraTestUtils extends Assert { } public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) { - Size windowSize = windowManager.getCurrentWindowMetrics().getSize(); + Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds(); - int width = windowSize.getWidth(); - int height = windowSize.getHeight(); + int width = windowBounds.width(); + int height = windowBounds.height(); if (height > width) { height = width; - width = windowSize.getHeight(); + width = windowBounds.height(); } if (bound.getWidth() <= width && diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java index d4eb2a9c75d0..a2da23e918d2 100644 --- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java +++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java @@ -28,11 +28,11 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.util.Size; import android.util.Slog; import android.view.Display; import android.view.ViewGroup; @@ -134,8 +134,8 @@ public class FakeApp extends Application { } lp.width = ViewGroup.LayoutParams.MATCH_PARENT; lp.height = ViewGroup.LayoutParams.MATCH_PARENT; - Size maxWindowSize = wm.getMaximumWindowMetrics().getSize(); - int maxSize = Math.max(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds(); + int maxSize = Math.max(maxWindowBounds.width(), maxWindowBounds.height()); maxSize *= 2; lp.x = maxSize; lp.y = maxSize; diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java index df00eee63b50..e2120f80f1c9 100644 --- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java +++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java @@ -19,22 +19,22 @@ package com.android.fakeoemfeatures; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import java.util.ArrayList; -import java.util.Random; - import android.app.Dialog; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.util.Size; import android.view.Display; import android.view.ViewGroup; import android.view.WindowManager; +import java.util.ArrayList; +import java.util.Random; + public class FakeBackgroundService extends Service { final ArrayList<int[]> mAllocs = new ArrayList<int[]>(); @@ -99,8 +99,8 @@ public class FakeBackgroundService extends Service { // Create an instance of WindowManager that is adjusted to the area of the display dedicated // for windows with type TYPE_APPLICATION_OVERLAY. final WindowManager wm = windowContext.getSystemService(WindowManager.class); - Size maxWindowSize = wm.getMaximumWindowMetrics().getSize(); - int maxSize = Math.max(maxWindowSize.getWidth(), maxWindowSize.getHeight()); + Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds(); + int maxSize = Math.max(maxWindowBounds.width(), maxWindowBounds.height()); maxSize *= 2; lp.x = maxSize; lp.y = maxSize; diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index a2fa46130076..a95677d0202f 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -406,6 +406,10 @@ public class PackageInstallerActivity extends AlertActivity { mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); mOk.setEnabled(false); + + if (!mOk.isInTouchMode()) { + mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus(); + } } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java index 2b841967d4a8..59735f413f9d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java @@ -189,19 +189,6 @@ public class AccessibilityUtils { return context.getString(R.string.config_defaultAccessibilityService); } - /** - * Check if the accessibility shortcut is enabled for a user - * - * @param context A valid context - * @param userId The user of interest - * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise. - * Note that the shortcut may be enabled, but no action associated with it. - */ - public static boolean isShortcutEnabled(Context context, int userId) { - return Settings.Secure.getIntForUser(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, userId) == 1; - } - private static Set<ComponentName> getInstalledServices(Context context) { final Set<ComponentName> installedServices = new HashSet<>(); installedServices.clear(); diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java index c34c365c1bfa..7ef080178a2f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java @@ -169,6 +169,22 @@ public class MetricsFeatureProvider { sourceMetricsCategory); } + /** + * Logs an event when the intent is started by Profile select dialog. + * + * @return true if the intent is loggable, otherwise false + */ + public boolean logStartedIntentWithProfile(Intent intent, int sourceMetricsCategory, + boolean isWorkProfile) { + if (intent == null) { + return false; + } + final ComponentName cn = intent.getComponent(); + final String key = cn != null ? cn.flattenToString() : intent.getAction(); + return logSettingsTileClick(key + (isWorkProfile ? "/work" : "/personal"), + sourceMetricsCategory); + } + private boolean logSettingsTileClick(String logKey, int sourceMetricsCategory) { if (TextUtils.isEmpty(logKey)) { // Not loggable diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 922caeb0a817..df0de68b1fb9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -69,9 +69,9 @@ public class LocalMediaManager implements BluetoothCallback { private MediaDevice mOnTransferBluetoothDevice; @VisibleForTesting - List<MediaDevice> mMediaDevices = new ArrayList<>(); + List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); @VisibleForTesting - List<MediaDevice> mDisconnectedMediaDevices = new ArrayList<>(); + List<MediaDevice> mDisconnectedMediaDevices = new CopyOnWriteArrayList<>(); @VisibleForTesting MediaDevice mPhoneDevice; @VisibleForTesting @@ -207,6 +207,7 @@ public class LocalMediaManager implements BluetoothCallback { public void stopScan() { mInfoMediaManager.unregisterCallback(mMediaDeviceCallback); mInfoMediaManager.stopScan(); + unRegisterDeviceAttributeChangeCallback(); } /** @@ -397,32 +398,34 @@ public class LocalMediaManager implements BluetoothCallback { } private List<MediaDevice> buildDisconnectedBluetoothDevice() { - for (MediaDevice device : mDisconnectedMediaDevices) { - ((BluetoothMediaDevice) device).getCachedDevice() - .unregisterCallback(mDeviceAttributeChangeCallback); - } - mDisconnectedMediaDevices.clear(); final List<BluetoothDevice> bluetoothDevices = mBluetoothAdapter.getMostRecentlyConnectedDevices(); final CachedBluetoothDeviceManager cachedDeviceManager = mLocalBluetoothManager.getCachedDeviceManager(); + final List<CachedBluetoothDevice> cachedBluetoothDeviceList = new ArrayList<>(); for (BluetoothDevice device : bluetoothDevices) { final CachedBluetoothDevice cachedDevice = cachedDeviceManager.findDevice(device); if (cachedDevice != null) { if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED && !cachedDevice.isConnected()) { - final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext, - cachedDevice, - null, null, mPackageName); - if (!mMediaDevices.contains(mediaDevice)) { - cachedDevice.registerCallback(mDeviceAttributeChangeCallback); - mDisconnectedMediaDevices.add(mediaDevice); - } + cachedBluetoothDeviceList.add(cachedDevice); } } } + + unRegisterDeviceAttributeChangeCallback(); + mDisconnectedMediaDevices.clear(); + for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) { + final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext, + cachedDevice, + null, null, mPackageName); + if (!mMediaDevices.contains(mediaDevice)) { + cachedDevice.registerCallback(mDeviceAttributeChangeCallback); + mDisconnectedMediaDevices.add(mediaDevice); + } + } return new ArrayList<>(mDisconnectedMediaDevices); } @@ -473,6 +476,12 @@ public class LocalMediaManager implements BluetoothCallback { } } + private void unRegisterDeviceAttributeChangeCallback() { + for (MediaDevice device : mDisconnectedMediaDevices) { + ((BluetoothMediaDevice) device).getCachedDevice() + .unregisterCallback(mDeviceAttributeChangeCallback); + } + } /** * Callback for notifying device information updating diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index 8bf48e59165e..c713d7813a54 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -17,6 +17,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; @@ -36,7 +37,10 @@ import com.android.settingslib.R; import java.util.List; -public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { +/** + * Track status of Wi-Fi for the Sys UI. + */ +public class WifiStatusTracker { private final Context mContext; private final WifiNetworkScoreCache mWifiNetworkScoreCache; private final WifiManager mWifiManager; @@ -55,8 +59,9 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { .clearCapabilities() .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build(); - private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager - .NetworkCallback() { + private final NetworkCallback mNetworkCallback = new NetworkCallback() { + // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable + // and onLinkPropertiesChanged. @Override public void onCapabilitiesChanged( Network network, NetworkCapabilities networkCapabilities) { @@ -64,11 +69,35 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { mCallback.run(); } }; + private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() { + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + // network is now the default network, and its capabilities are nc. + // This method will always be called immediately after the network becomes the + // default, in addition to any time the capabilities change while the network is + // the default. + mDefaultNetwork = network; + mDefaultNetworkCapabilities = nc; + updateStatusLabel(); + mCallback.run(); + } + @Override + public void onLost(Network network) { + // The system no longer has a default network. + mDefaultNetwork = null; + mDefaultNetworkCapabilities = null; + updateStatusLabel(); + mCallback.run(); + } + }; + private Network mDefaultNetwork = null; + private NetworkCapabilities mDefaultNetworkCapabilities = null; private final Runnable mCallback; private WifiInfo mWifiInfo; public boolean enabled; public boolean isCaptivePortal; + public boolean isDefaultNetwork; public int state; public boolean connected; public String ssid; @@ -94,11 +123,13 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { mWifiNetworkScoreCache.registerListener(mCacheListener); mConnectivityManager.registerNetworkCallback( mNetworkRequest, mNetworkCallback, mHandler); + mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler); } else { mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mWifiNetworkScoreCache); mWifiNetworkScoreCache.unregisterListener(); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); + mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback); } } @@ -154,8 +185,17 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { } private void updateStatusLabel() { - final NetworkCapabilities networkCapabilities - = mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork()); + NetworkCapabilities networkCapabilities; + final Network currentWifiNetwork = mWifiManager.getCurrentNetwork(); + if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) { + // Wifi is connected and the default network. + isDefaultNetwork = true; + networkCapabilities = mDefaultNetworkCapabilities; + } else { + isDefaultNetwork = false; + networkCapabilities = mConnectivityManager.getNetworkCapabilities( + mWifiManager.getCurrentNetwork()); + } isCaptivePortal = false; if (networkCapabilities != null) { if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java index ed0857ca21dc..204a93333d81 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java @@ -164,6 +164,38 @@ public class MetricsFeatureProviderTest { } @Test + public void logStartedIntentWithProfile_isPersonalProfile_shouldTagPersonal() { + final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); + + final boolean loggable = mProvider.logStartedIntentWithProfile(intent, + MetricsEvent.SETTINGS_GESTURES, false); + + assertThat(loggable).isTrue(); + verify(mLogWriter).action( + MetricsEvent.SETTINGS_GESTURES, + MetricsEvent.ACTION_SETTINGS_TILE_CLICK, + SettingsEnums.PAGE_UNKNOWN, + "pkg/cls/personal", + 0); + } + + @Test + public void logStartedIntentWithProfile_isWorkProfile_shouldTagWork() { + final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls")); + + final boolean loggable = mProvider.logStartedIntentWithProfile(intent, + MetricsEvent.SETTINGS_GESTURES, true); + + assertThat(loggable).isTrue(); + verify(mLogWriter).action( + MetricsEvent.SETTINGS_GESTURES, + MetricsEvent.ACTION_SETTINGS_TILE_CLICK, + SettingsEnums.PAGE_UNKNOWN, + "pkg/cls/work", + 0); + } + + @Test public void getAttribution_noActivity_shouldReturnUnknown() { assertThat(mProvider.getAttribution(null /* activity */)) .isEqualTo(SettingsEnums.PAGE_UNKNOWN); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index d350d9df37b8..d320df9c24ba 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -48,7 +48,6 @@ public class SecureSettings { Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 4d33b627f4e2..8801a9c32a36 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -25,10 +25,10 @@ import static android.provider.settings.validators.SettingsValidators.COMMA_SEPA import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR; -import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR; @@ -82,7 +82,6 @@ public class SecureSettingsValidators { Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put( diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index af74121a11c9..b22caf0edc66 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1781,9 +1781,6 @@ class SettingsProtoDumpUtil { Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, SecureSettingsProto.Accessibility.LARGE_POINTER_ICON); dumpSetting(s, p, - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, - SecureSettingsProto.Accessibility.SHORTCUT_ENABLED); - dumpSetting(s, p, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, SecureSettingsProto.Accessibility.SHORTCUT_ON_LOCK_SCREEN); dumpSetting(s, p, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 5a9d7497b641..2fde87c08ad5 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3436,7 +3436,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 188; + private static final int SETTINGS_VERSION = 189; private final int mUserId; @@ -4759,6 +4759,23 @@ public class SettingsProvider extends ContentProvider { currentVersion = 188; } + if (currentVersion == 188) { + // Deprecate ACCESSIBILITY_SHORTCUT_ENABLED, and migrate it + // to ACCESSIBILITY_SHORTCUT_TARGET_SERVICE. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting shortcutEnabled = secureSettings.getSettingLocked( + "accessibility_shortcut_enabled"); + if ("0".equals(shortcutEnabled.getValue())) { + // Clear shortcut key targets list setting. + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, + "", null /* tag */, false /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + secureSettings.deleteSettingLocked("accessibility_shortcut_enabled"); + currentVersion = 189; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index c7fb00a8130c..4771c4139a5b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -81,6 +81,8 @@ <uses-permission android:name="android.permission.READ_INPUT_STATE" /> <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> + <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient --> + <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" /> <uses-permission android:name="android.permission.CLEAR_APP_CACHE" /> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 64e52376effd..0ae00e1ac8b5 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -1019,8 +1019,11 @@ public class BugreportProgressService extends Service { * Wraps up bugreport generation and triggers a notification to share the bugreport. */ private void onBugreportFinished(BugreportInfo info) { + if (!TextUtils.isEmpty(info.shareTitle)) { + info.setTitle(info.shareTitle); + } Log.d(TAG, "Bugreport finished with title: " + info.getTitle() - + " and shareDescription: " + info.shareDescription); + + " and shareDescription: " + info.shareDescription); info.finished = new AtomicBoolean(true); synchronized (mLock) { @@ -1795,7 +1798,9 @@ public class BugreportProgressService extends Service { /** * User-provided, detailed description of the bugreport; when set, will be added to the body - * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. + * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. This is shown in the app where the + * bugreport is being shared as an attachment. This is not related/dependant on + * {@code shareDescription}. */ private String description; @@ -2130,7 +2135,6 @@ public class BugreportProgressService extends Service { return new BugreportInfo[size]; } }; - } @GuardedBy("mLock") diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml index 7708b8e9db6c..6e1fd2072b32 100644 --- a/packages/SystemUI/res/layout/controls_base_item.xml +++ b/packages/SystemUI/res/layout/controls_base_item.xml @@ -30,8 +30,8 @@ <ImageView android:id="@+id/icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="@dimen/control_icon_size" + android:layout_height="@dimen/control_icon_size" android:paddingTop="@dimen/control_padding_adjustment" android:clickable="false" android:focusable="false" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 432cd749abbd..ee7f5230145e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1225,6 +1225,7 @@ <dimen name="controls_top_margin">44dp</dimen> <dimen name="control_header_text_size">22sp</dimen> <dimen name="control_text_size">14sp</dimen> + <dimen name="control_icon_size">24dp</dimen> <dimen name="control_spacing">4dp</dimen> <dimen name="control_list_divider">1dp</dimen> <dimen name="control_corner_radius">12dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index c8c35c704297..8a3a16e9a6cf 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -374,9 +374,31 @@ <string name="biometric_dialog_wrong_password">Wrong password</string> <!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]--> <string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string> + <!-- Error string shown when the user enters an incorrect PIN/pattern/password and it counts towards the max attempts before the data on the device is wiped. [CHAR LIMIT=NONE]--> <string name="biometric_dialog_credential_attempts_before_wipe">Try again. Attempt <xliff:g id="attempts" example="1">%1$d</xliff:g> of <xliff:g id="max_attempts" example="3">%2$d</xliff:g>.</string> + <!-- Title of a dialog shown when the user only has one attempt left to provide the correct PIN/pattern/password before the device, one of its users, or a work profile is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_attempt_before_wipe_dialog_title">Your data will be deleted</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct lock pattern before the device is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_device">If you enter an incorrect pattern on the next attempt, this device\u2019s data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the device is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pin_attempt_before_wipe_device">If you enter an incorrect PIN on the next attempt, this device\u2019s data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the device is wiped. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_password_attempt_before_wipe_device">If you enter an incorrect password on the next attempt, this device\u2019s data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct lock pattern before the user is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_user">If you enter an incorrect pattern on the next attempt, this user will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the user is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pin_attempt_before_wipe_user">If you enter an incorrect PIN on the next attempt, this user will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the user is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_password_attempt_before_wipe_user">If you enter an incorrect password on the next attempt, this user will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct pattern before the work profile is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile">If you enter an incorrect pattern on the next attempt, your work profile and its data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the work profile is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_pin_attempt_before_wipe_profile">If you enter an incorrect PIN on the next attempt, your work profile and its data will be deleted.</string> + <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the work profile is removed. [CHAR LIMIT=NONE] --> + <string name="biometric_dialog_last_password_attempt_before_wipe_profile">If you enter an incorrect password on the next attempt, your work profile and its data will be deleted.</string> + <!-- Content of a dialog shown when the user has failed to provide the device lock too many times and the device is wiped. [CHAR LIMIT=NONE] --> <string name="biometric_dialog_failed_attempts_now_wiping_device">Too many incorrect attempts. This device\u2019s data will be deleted.</string> <!-- Content of a dialog shown when the user has failed to provide the user lock too many times and the user is removed. [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 49e3e5724988..3bda3c8df699 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -20,9 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -38,7 +36,6 @@ import android.app.ActivityTaskManager; import android.app.AppGlobals; import android.app.IAssistDataReceiver; import android.app.WindowConfiguration; -import android.app.WindowConfiguration.ActivityType; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -113,15 +110,18 @@ public class ActivityManagerWrapper { * @return the top running task (can be {@code null}). */ public ActivityManager.RunningTaskInfo getRunningTask() { - return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */); + return getRunningTask(false /* filterVisibleRecents */); } - public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) { + /** + * @return the top running task filtering only for tasks that can be visible in the recent tasks + * list (can be {@code null}). + */ + public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { // Note: The set of running tasks from the system is ordered by recency try { List<ActivityManager.RunningTaskInfo> tasks = - ActivityTaskManager.getService().getFilteredTasks(1, ignoreActivityType, - WINDOWING_MODE_PINNED /* ignoreWindowingMode */); + ActivityTaskManager.getService().getFilteredTasks(1, filterOnlyVisibleRecents); if (tasks.isEmpty()) { return null; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 3afe19f926ec..7cbc840afed4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -127,6 +127,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; private static final boolean DEBUG_FACE = true; + private static final boolean DEBUG_SPEW = false; private static final int LOW_BATTERY_THRESHOLD = 20; private static final String ACTION_FACE_UNLOCK_STARTED @@ -324,7 +325,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; - private class BiometricAuthenticated { + @VisibleForTesting + static class BiometricAuthenticated { private final boolean mAuthenticated; private final boolean mIsStrongBiometric; @@ -338,11 +340,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray(); - private SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); - private SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>(); + @VisibleForTesting + SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>(); + @VisibleForTesting + SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>(); + private static int sCurrentUser; private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState; @@ -1850,11 +1855,33 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. - return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant()) + final boolean shouldListen = + (mBouncer || mAuthInterruptActive || awakeKeyguard + || shouldListenForFaceAssistant()) && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed && strongAuthAllowsScanning && mIsPrimaryUser && !mSecureCameraLaunched; + + // Too chatty, but very useful when debugging issues. + if (DEBUG_SPEW) { + Log.v(TAG, "shouldListenForFace(" + user + ")=" + shouldListen + "... " + + ", mBouncer: " + mBouncer + + ", mAuthInterruptActive: " + mAuthInterruptActive + + ", awakeKeyguard: " + awakeKeyguard + + ", shouldListenForFaceAssistant: " + shouldListenForFaceAssistant() + + ", mSwitchingUser: " + mSwitchingUser + + ", isFaceDisabled(" + user + "): " + isFaceDisabled(user) + + ", becauseCannotSkipBouncer: " + becauseCannotSkipBouncer + + ", mKeyguardGoingAway: " + mKeyguardGoingAway + + ", mFaceSettingEnabledForUser(" + user + "): " + + mFaceSettingEnabledForUser.get(user) + + ", mLockIconPressed: " + mLockIconPressed + + ", strongAuthAllowsScanning: " + strongAuthAllowsScanning + + ", isPrimaryUser: " + mIsPrimaryUser + + ", mSecureCameraLaunched: " + mSecureCameraLaunched); + } + return shouldListen; } /** @@ -2049,8 +2076,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab /** * Handle {@link #MSG_USER_SWITCHING} */ - private void handleUserSwitching(int userId, IRemoteCallback reply) { + @VisibleForTesting + void handleUserSwitching(int userId, IRemoteCallback reply) { Assert.isMainThread(); + clearBiometricRecognized(); mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId)); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index 13f3c0fce5c2..b006bc1351a3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -347,21 +347,35 @@ public abstract class AuthCredentialView extends LinearLayout { showError(message); } - // Only show popup dialog before wipe. + // Only show dialog if <=1 attempts are left before wiping. final int remainingAttempts = maxAttempts - numAttempts; - if (remainingAttempts <= 0) { - showNowWipingMessage(); - mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR); + if (remainingAttempts == 1) { + showLastAttemptBeforeWipeDialog(); + } else if (remainingAttempts <= 0) { + showNowWipingDialog(); } return true; } - private void showNowWipingMessage() { + private void showLastAttemptBeforeWipeDialog() { + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) + .setMessage( + getLastAttemptBeforeWipeMessageRes(getUserTypeForWipe(), mCredentialType)) + .setPositiveButton(android.R.string.ok, null) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + alertDialog.show(); + } + + private void showNowWipingDialog() { final AlertDialog alertDialog = new AlertDialog.Builder(mContext) .setMessage(getNowWipingMessageRes(getUserTypeForWipe())) .setPositiveButton(R.string.biometric_dialog_now_wiping_dialog_dismiss, null) + .setOnDismissListener( + dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR)) .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); alertDialog.show(); } @@ -377,6 +391,59 @@ public abstract class AuthCredentialView extends LinearLayout { } } + private static @StringRes int getLastAttemptBeforeWipeMessageRes( + @UserType int userType, @Utils.CredentialType int credentialType) { + switch (userType) { + case USER_TYPE_PRIMARY: + return getLastAttemptBeforeWipeDeviceMessageRes(credentialType); + case USER_TYPE_MANAGED_PROFILE: + return getLastAttemptBeforeWipeProfileMessageRes(credentialType); + case USER_TYPE_SECONDARY: + return getLastAttemptBeforeWipeUserMessageRes(credentialType); + default: + throw new IllegalArgumentException("Unrecognized user type:" + userType); + } + } + + private static @StringRes int getLastAttemptBeforeWipeDeviceMessageRes( + @Utils.CredentialType int credentialType) { + switch (credentialType) { + case Utils.CREDENTIAL_PIN: + return R.string.biometric_dialog_last_pin_attempt_before_wipe_device; + case Utils.CREDENTIAL_PATTERN: + return R.string.biometric_dialog_last_pattern_attempt_before_wipe_device; + case Utils.CREDENTIAL_PASSWORD: + default: + return R.string.biometric_dialog_last_password_attempt_before_wipe_device; + } + } + + private static @StringRes int getLastAttemptBeforeWipeProfileMessageRes( + @Utils.CredentialType int credentialType) { + switch (credentialType) { + case Utils.CREDENTIAL_PIN: + return R.string.biometric_dialog_last_pin_attempt_before_wipe_profile; + case Utils.CREDENTIAL_PATTERN: + return R.string.biometric_dialog_last_pattern_attempt_before_wipe_profile; + case Utils.CREDENTIAL_PASSWORD: + default: + return R.string.biometric_dialog_last_password_attempt_before_wipe_profile; + } + } + + private static @StringRes int getLastAttemptBeforeWipeUserMessageRes( + @Utils.CredentialType int credentialType) { + switch (credentialType) { + case Utils.CREDENTIAL_PIN: + return R.string.biometric_dialog_last_pin_attempt_before_wipe_user; + case Utils.CREDENTIAL_PATTERN: + return R.string.biometric_dialog_last_pattern_attempt_before_wipe_user; + case Utils.CREDENTIAL_PASSWORD: + default: + return R.string.biometric_dialog_last_password_attempt_before_wipe_user; + } + } + private static @StringRes int getNowWipingMessageRes(@UserType int userType) { switch (userType) { case USER_TYPE_PRIMARY: diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index 8bc35f4058e9..2bd15188b7d3 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -208,7 +208,7 @@ public class BubbleData { b -> { notificationEntryUpdated(bubble, /* suppressFlyout */ false, /* showInShade */ true); - setSelectedBubbleInternal(bubble); + setSelectedBubble(bubble); }, mContext, stack, factory); dispatchPendingChanges(); @@ -761,6 +761,17 @@ public class BubbleData { } @VisibleForTesting(visibility = PRIVATE) + Bubble getOverflowBubbleWithKey(String key) { + for (int i = 0; i < mOverflowBubbles.size(); i++) { + Bubble bubble = mOverflowBubbles.get(i); + if (bubble.getKey().equals(key)) { + return bubble; + } + } + return null; + } + + @VisibleForTesting(visibility = PRIVATE) void setTimeSource(TimeSource timeSource) { mTimeSource = timeSource; } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 6c49c82acdc0..118fcbb20f26 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -252,10 +252,17 @@ class ControlsControllerImpl @Inject constructor ( it.controlId in favoritesForComponentKeys ) } + val removedControls = mutableListOf<ControlStatus>() + Favorites.getStructuresForComponent(componentName).forEach { st -> + st.controls.forEach { + if (it.controlId in removed) { + val r = createRemovedStatus(componentName, it, st.structure) + removedControls.add(r) + } + } + } val loadData = createLoadDataObject( - Favorites.getControlsForComponent(componentName) - .filter { it.controlId in removed } - .map { createRemovedStatus(componentName, it) } + + removedControls + controlsWithFavorite, favoritesForComponentKeys ) @@ -266,17 +273,15 @@ class ControlsControllerImpl @Inject constructor ( override fun error(message: String) { loadCanceller = null executor.execute { - val loadData = Favorites.getControlsForComponent(componentName) - .let { controls -> - val keys = controls.map { it.controlId } - createLoadDataObject( - controls.map { - createRemovedStatus(componentName, it, false) - }, - keys, - true - ) - } + val controls = Favorites.getStructuresForComponent(componentName) + .flatMap { st -> + st.controls.map { + createRemovedStatus(componentName, it, st.structure, + false) + } + } + val keys = controls.map { it.control.controlId } + val loadData = createLoadDataObject(controls, keys, true) dataCallback.accept(loadData) } } @@ -372,6 +377,7 @@ class ControlsControllerImpl @Inject constructor ( private fun createRemovedStatus( componentName: ComponentName, controlInfo: ControlInfo, + structure: CharSequence, setRemoved: Boolean = true ): ControlStatus { val intent = Intent(Intent.ACTION_MAIN).apply { @@ -384,6 +390,8 @@ class ControlsControllerImpl @Inject constructor ( 0) val control = Control.StatelessBuilder(controlInfo.controlId, pendingIntent) .setTitle(controlInfo.controlTitle) + .setSubtitle(controlInfo.controlSubtitle) + .setStructure(structure) .setDeviceType(controlInfo.deviceType) .build() return ControlStatus(control, componentName, true, setRemoved) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt index 15c2a0afe819..a7a41033bb5d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt @@ -34,24 +34,29 @@ import com.android.systemui.R /** * Creates all dialogs for challengeValues that can occur from a call to - * {@link ControlsProviderService#performControlAction}. The types of challenge - * responses are listed in {@link ControlAction.ResponseResult}. + * [ControlsProviderService#performControlAction]. The types of challenge responses are listed in + * [ControlAction.ResponseResult]. */ object ChallengeDialogs { - fun createPinDialog(cvh: ControlViewHolder): Dialog? { + private const val WINDOW_TYPE = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY + private const val STYLE = android.R.style.Theme_DeviceDefault_Dialog_Alert + + /** + * AlertDialogs to handle [ControlAction#RESPONSE_CHALLENGE_PIN] and + * [ControlAction#RESPONSE_CHALLENGE_PIN] responses, decided by the useAlphaNumeric + * parameter. + */ + fun createPinDialog(cvh: ControlViewHolder, useAlphaNumeric: Boolean): Dialog? { val lastAction = cvh.lastAction if (lastAction == null) { Log.e(ControlsUiController.TAG, "PIN Dialog attempted but no last action is set. Will not show") return null } - val builder = AlertDialog.Builder( - cvh.context, - android.R.style.Theme_DeviceDefault_Dialog_Alert - ).apply { + val builder = AlertDialog.Builder(cvh.context, STYLE).apply { val res = cvh.context.resources - setTitle(res.getString(R.string.controls_pin_verify, *arrayOf(cvh.title.getText()))) + setTitle(res.getString(R.string.controls_pin_verify, cvh.title.getText())) setView(R.layout.controls_dialog_pin) setPositiveButton( android.R.string.ok, @@ -71,25 +76,64 @@ object ChallengeDialogs { } return builder.create().apply { getWindow().apply { - setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY) + setType(WINDOW_TYPE) setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE) } setOnShowListener(DialogInterface.OnShowListener { _ -> val editText = requireViewById<EditText>(R.id.controls_pin_input) - requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { v -> - if ((v as CheckBox).isChecked) { - editText.setInputType( - InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD) - } else { - editText.setInputType( - InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD) - } + val useAlphaCheckBox = requireViewById<CheckBox>(R.id.controls_pin_use_alpha) + useAlphaCheckBox.setChecked(useAlphaNumeric) + setInputType(editText, useAlphaCheckBox.isChecked()) + requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { _ -> + setInputType(editText, useAlphaCheckBox.isChecked()) } editText.requestFocus() }) } } + /** + * AlertDialogs to handle [ControlAction#RESPONSE_CHALLENGE_ACK] response type. + */ + fun createConfirmationDialog(cvh: ControlViewHolder): Dialog? { + val lastAction = cvh.lastAction + if (lastAction == null) { + Log.e(ControlsUiController.TAG, + "Confirmation Dialog attempted but no last action is set. Will not show") + return null + } + val builder = AlertDialog.Builder(cvh.context, STYLE).apply { + val res = cvh.context.resources + setMessage(res.getString( + R.string.controls_confirmation_message, cvh.title.getText())) + setPositiveButton( + android.R.string.ok, + DialogInterface.OnClickListener { dialog, _ -> + cvh.action(addChallengeValue(lastAction, "true")) + dialog.dismiss() + }) + setNegativeButton( + android.R.string.cancel, + DialogInterface.OnClickListener { dialog, _ -> dialog.cancel() } + ) + } + return builder.create().apply { + getWindow().apply { + setType(WINDOW_TYPE) + } + } + } + + private fun setInputType(editText: EditText, useTextInput: Boolean) { + if (useTextInput) { + editText.setInputType( + InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD) + } else { + editText.setInputType( + InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD) + } + } + private fun addChallengeValue(action: ControlAction, challengeValue: String): ControlAction { val id = action.getTemplateId() return when (action) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 9f5dd02b4514..7d3a86091869 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -21,6 +21,7 @@ import android.graphics.drawable.ClipDrawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.LayerDrawable import android.service.controls.Control +import android.service.controls.DeviceTypes import android.service.controls.actions.ControlAction import android.service.controls.templates.ControlTemplate import android.service.controls.templates.StatelessTemplate @@ -156,7 +157,11 @@ class ControlViewHolder( statusExtra.setTextColor(fg) icon.setImageDrawable(ri.icon) - icon.setImageTintList(fg) + + // do not color app icons + if (deviceType != DeviceTypes.TYPE_ROUTINE) { + icon.setImageTintList(fg) + } (clipLayer.getDrawable() as GradientDrawable).apply { setColor(context.getResources().getColor(bg, context.getTheme())) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index b0db4370f38d..05a0c45c2e15 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -442,7 +442,15 @@ class ControlsUiControllerImpl @Inject constructor ( controlViewsById.get(key)?.let { cvh -> when (response) { ControlAction.RESPONSE_CHALLENGE_PIN -> { - activeDialog = ChallengeDialogs.createPinDialog(cvh) + activeDialog = ChallengeDialogs.createPinDialog(cvh, false) + activeDialog?.show() + } + ControlAction.RESPONSE_CHALLENGE_PASSPHRASE -> { + activeDialog = ChallengeDialogs.createPinDialog(cvh, true) + activeDialog?.show() + } + ControlAction.RESPONSE_CHALLENGE_ACK -> { + activeDialog = ChallengeDialogs.createConfirmationDialog(cvh) activeDialog?.show() } else -> cvh.actionResponse(response) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index 27e46497b20a..810ea65c7873 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -64,7 +64,7 @@ data class RenderInfo( val iconState = deviceIconMap.getValue(iconKey) val resourceId = iconState[enabled] - var icon: Drawable? = null + var icon: Drawable? if (resourceId == APP_ICON_ID) { icon = appIconMap.get(componentName) if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index c495c58fff2a..f79c8b2393d0 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -64,12 +64,13 @@ class ToggleRangeBehavior : Behavior { val gestureListener = ToggleRangeGestureListener(cvh.layout) val gestureDetector = GestureDetector(context, gestureListener) - cvh.layout.setOnTouchListener { _: View, e: MotionEvent -> + cvh.layout.setOnTouchListener { v: View, e: MotionEvent -> if (gestureDetector.onTouchEvent(e)) { return@setOnTouchListener true } if (e.getAction() == MotionEvent.ACTION_UP && gestureListener.isDragging) { + v.getParent().requestDisallowInterceptTouchEvent(false) gestureListener.isDragging = false endUpdateRange() return@setOnTouchListener true @@ -254,6 +255,7 @@ class ToggleRangeBehavior : Behavior { yDiff: Float ): Boolean { if (!isDragging) { + v.getParent().requestDisallowInterceptTouchEvent(true) this@ToggleRangeBehavior.beginUpdateRange() isDragging = true } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 2cc3d9e22a7d..96494cfe640f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -305,7 +305,8 @@ public class KeyguardSliceProvider extends SliceProvider implements oldInstance.onDestroy(); } mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern); - mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0); + mPendingIntent = PendingIntent.getActivity(getContext(), 0, + new Intent(getContext(), KeyguardSliceProvider.class), 0); mMediaManager.addCallback(this); mStatusBarStateController.addCallback(this); mNextAlarmController.addCallback(this); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java index 1a01cfedb82e..0b076559ae36 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java @@ -99,14 +99,14 @@ public class PipResizeGestureHandler { mEnablePipResize = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_USER_RESIZE, - /* defaultValue = */ false); + /* defaultValue = */ true); deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor, new DeviceConfig.OnPropertiesChangedListener() { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { if (properties.getKeyset().contains(PIP_USER_RESIZE)) { mEnablePipResize = properties.getBoolean( - PIP_USER_RESIZE, /* defaultValue = */ false); + PIP_USER_RESIZE, /* defaultValue = */ true); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 5ccf8c7e9212..33cc086a8d9f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -133,6 +133,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne new LocalMediaManager.DeviceCallback() { @Override public void onDeviceListUpdate(List<MediaDevice> devices) { + if (mLocalMediaManager == null) { + return; + } MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); // Check because this can be called several times while changing devices if (mDevice == null || !mDevice.equals(currentDevice)) { @@ -293,14 +296,17 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mMediaPlayers.size() > 0) { ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE); - // Set up listener for device changes - // TODO: integrate with MediaTransferManager? - InfoMediaManager imm = - new InfoMediaManager(mContext, null, null, mLocalBluetoothManager); - mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm, null); - mLocalMediaManager.startScan(); - mDevice = mLocalMediaManager.getCurrentConnectedDevice(); - mLocalMediaManager.registerCallback(mDeviceCallback); + if (mLocalMediaManager == null) { + // Set up listener for device changes + // TODO: integrate with MediaTransferManager? + InfoMediaManager imm = + new InfoMediaManager(mContext, null, null, mLocalBluetoothManager); + mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm, + null); + mLocalMediaManager.startScan(); + mDevice = mLocalMediaManager.getCurrentConnectedDevice(); + mLocalMediaManager.registerCallback(mDeviceCallback); + } } } @@ -323,8 +329,11 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mMediaCarousel.removeView(player.getView()); if (mMediaPlayers.size() == 0) { ((View) mMediaCarousel.getParent()).setVisibility(View.GONE); - mLocalMediaManager.stopScan(); - mLocalMediaManager.unregisterCallback(mDeviceCallback); + if (mLocalMediaManager != null) { + mLocalMediaManager.stopScan(); + mLocalMediaManager.unregisterCallback(mDeviceCallback); + mLocalMediaManager = null; + } } return true; } @@ -397,6 +406,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mLocalMediaManager != null) { mLocalMediaManager.stopScan(); mLocalMediaManager.unregisterCallback(mDeviceCallback); + mLocalMediaManager = null; } super.onDetachedFromWindow(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index aa6444973a6f..3b3d9dde3b7e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -17,7 +17,6 @@ package com.android.systemui.recents; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.Prefs.Key.DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT; @@ -27,8 +26,7 @@ import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDIN import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT; import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_FROM_HOME_COUNT; import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS; -import static com.android.systemui.shared.system.LauncherEventUtil - .RECENTS_QUICK_SCRUB_ONBOARDING_TIP; +import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP; import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP; import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE; @@ -139,7 +137,7 @@ public class RecentsOnboarding { private void onAppLaunch() { ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance() - .getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */); + .getRunningTask(); if (info == null) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index adca10ff7677..ecfe1168b7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -110,10 +110,14 @@ public class EdgeBackGestureHandler implements DisplayListener, private final float mTouchSlop; // Duration after which we consider the event as longpress. private final int mLongPressTimeout; + // The back gesture type + private int mBackType; private final PointF mDownPoint = new PointF(); + private final PointF mEndPoint = new PointF(); private boolean mThresholdCrossed = false; private boolean mAllowGesture = false; + private boolean mLogGesture = false; private boolean mInRejectedExclusion = false; private boolean mIsOnLeftEdge; @@ -141,24 +145,16 @@ public class EdgeBackGestureHandler implements DisplayListener, mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x, (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge); - int backtype = (mInRejectedExclusion - ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED : - SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED); - SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype, - (int) mDownPoint.y, mIsOnLeftEdge - ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT : - SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT); + logGesture(mInRejectedExclusion + ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED + : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED); } @Override public void cancelBack() { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE); mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x, (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge); - int backtype = SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE; - SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype, - (int) mDownPoint.y, mIsOnLeftEdge - ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT : - SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT); } }; @@ -331,39 +327,55 @@ public class EdgeBackGestureHandler implements DisplayListener, } private boolean isWithinTouchRegion(int x, int y) { - // Disallow if too far from the edge - if (x > mEdgeWidthLeft + mLeftInset - && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) { + // Disallow if we are in the bottom gesture area + if (y >= (mDisplaySize.y - mBottomGestureHeight)) { return false; } - // Disallow if we are in the bottom gesture area - if (y >= (mDisplaySize.y - mBottomGestureHeight)) { + // If the point is way too far (twice the margin), it is + // not interesting to us for logging purposes, nor we + // should process it. Simply return false and keep + // mLogGesture = false. + if (x > 2 * (mEdgeWidthLeft + mLeftInset) + && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) { return false; } + // Denotes whether we should proceed with the gesture. + // Even if it is false, we may want to log it assuming + // it is not invalid due to exclusion. + boolean withinRange = x <= mEdgeWidthLeft + mLeftInset + || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset); + // Always allow if the user is in a transient sticky immersive state if (mIsNavBarShownTransiently) { - return true; + mLogGesture = true; + return withinRange; } - boolean isInExcludedRegion = mExcludeRegion.contains(x, y); - if (isInExcludedRegion) { - mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1, - false /* isButton */, !mIsOnLeftEdge); - SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, - SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED, y, - mIsOnLeftEdge ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT : - SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT); - } else { - mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y); + if (mExcludeRegion.contains(x, y)) { + if (withinRange) { + // Log as exclusion only if it is in acceptable range in the first place. + mOverviewProxyService.notifyBackAction( + false /* completed */, -1, -1, false /* isButton */, !mIsOnLeftEdge); + // We don't have the end point for logging purposes. + mEndPoint.x = -1; + mEndPoint.y = -1; + mLogGesture = true; + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED); + } + return false; } - return !isInExcludedRegion; + + mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y); + mLogGesture = true; + return withinRange; } private void cancelGesture(MotionEvent ev) { // Send action cancel to reset all the touch events mAllowGesture = false; + mLogGesture = false; mInRejectedExclusion = false; MotionEvent cancelEv = MotionEvent.obtain(ev); cancelEv.setAction(MotionEvent.ACTION_CANCEL); @@ -371,51 +383,86 @@ public class EdgeBackGestureHandler implements DisplayListener, cancelEv.recycle(); } + private void logGesture(int backType) { + if (!mLogGesture) { + return; + } + mLogGesture = false; + SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType, + (int) mDownPoint.y, mIsOnLeftEdge + ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT + : SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT, + (int) mDownPoint.x, (int) mDownPoint.y, + (int) mEndPoint.x, (int) mEndPoint.y, + mEdgeWidthLeft + mLeftInset, + mDisplaySize.x - (mEdgeWidthRight + mRightInset)); + } + private void onMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset; + mLogGesture = false; mInRejectedExclusion = false; mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags) && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); if (mAllowGesture) { mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); mEdgeBackPlugin.onMotionEvent(ev); - + } + if (mLogGesture) { mDownPoint.set(ev.getX(), ev.getY()); + mEndPoint.set(-1, -1); mThresholdCrossed = false; } - - } else if (mAllowGesture) { + } else if (mAllowGesture || mLogGesture) { if (!mThresholdCrossed) { + mEndPoint.x = (int) ev.getX(); + mEndPoint.y = (int) ev.getY(); if (action == MotionEvent.ACTION_POINTER_DOWN) { - // We do not support multi touch for back gesture - cancelGesture(ev); + if (mAllowGesture) { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH); + // We do not support multi touch for back gesture + cancelGesture(ev); + } + mLogGesture = false; return; } else if (action == MotionEvent.ACTION_MOVE) { if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) { - cancelGesture(ev); + if (mAllowGesture) { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS); + cancelGesture(ev); + } + mLogGesture = false; return; } float dx = Math.abs(ev.getX() - mDownPoint.x); float dy = Math.abs(ev.getY() - mDownPoint.y); if (dy > dx && dy > mTouchSlop) { - cancelGesture(ev); + if (mAllowGesture) { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE); + cancelGesture(ev); + } + mLogGesture = false; return; - } else if (dx > dy && dx > mTouchSlop) { - mThresholdCrossed = true; - // Capture inputs - mInputMonitor.pilferPointers(); + if (mAllowGesture) { + mThresholdCrossed = true; + // Capture inputs + mInputMonitor.pilferPointers(); + } else { + logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE); + } } } - } - // forward touch - mEdgeBackPlugin.onMotionEvent(ev); + if (mAllowGesture) { + // forward touch + mEdgeBackPlugin.onMotionEvent(ev); + } } Dependency.get(ProtoTracer.class).update(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 7c963869ed47..c2fc18fe21a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -37,10 +37,10 @@ import java.util.Objects; public class WifiSignalController extends SignalController<WifiSignalController.WifiState, SignalController.IconGroup> { - private final boolean mHasMobileData; + private final boolean mHasMobileDataFeature; private final WifiStatusTracker mWifiTracker; - public WifiSignalController(Context context, boolean hasMobileData, + public WifiSignalController(Context context, boolean hasMobileDataFeature, CallbackHandler callbackHandler, NetworkControllerImpl networkController, WifiManager wifiManager) { super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI, @@ -52,7 +52,7 @@ public class WifiSignalController extends mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager, connectivityManager, this::handleStatusUpdated); mWifiTracker.setListening(true); - mHasMobileData = hasMobileData; + mHasMobileDataFeature = hasMobileDataFeature; if (wifiManager != null) { wifiManager.registerTrafficStateCallback(context.getMainExecutor(), new WifiTrafficStateCallback()); @@ -85,9 +85,10 @@ public class WifiSignalController extends // only show wifi in the cluster if connected or if wifi-only boolean visibleWhenEnabled = mContext.getResources().getBoolean( R.bool.config_showWifiIndicatorWhenEnabled); - boolean wifiVisible = mCurrentState.enabled - && ((mCurrentState.connected && mCurrentState.inetCondition == 1) - || !mHasMobileData || visibleWhenEnabled); + boolean wifiVisible = mCurrentState.enabled && ( + (mCurrentState.connected && mCurrentState.inetCondition == 1) + || !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork + || visibleWhenEnabled); String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getTextIfExists(getContentDescription()).toString(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 9d9ba1bc91b9..eecde7218d28 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -52,6 +52,7 @@ import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.Handler; +import android.os.IRemoteCallback; import android.os.UserHandle; import android.os.UserManager; import android.telephony.ServiceState; @@ -63,6 +64,7 @@ import android.testing.TestableContext; import android.testing.TestableLooper; import com.android.internal.telephony.TelephonyIntents; +import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; @@ -506,6 +508,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void testBiometricsCleared_whenUserSwitches() throws Exception { + final IRemoteCallback reply = new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) {} // do nothing + }; + final BiometricAuthenticated dummyAuthentication = + new BiometricAuthenticated(true /* authenticated */, true /* strong */); + mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication); + mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.put(0 /* user */, dummyAuthentication); + assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1); + assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1); + + mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, reply); + assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0); + assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0); + } + + @Test public void testGetUserCanSkipBouncer_whenTrust() { int user = KeyguardUpdateMonitor.getCurrentUser(); mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 4f16031741bc..6f3fbb9cbd2c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -92,6 +92,8 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.InjectionInflationController; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -318,6 +320,23 @@ public class BubbleControllerTest extends SysuiTestCase { } @Test + public void testPromoteBubble_autoExpand() { + mBubbleController.updateBubble(mRow2.getEntry()); + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.removeBubble( + mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE); + + Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey()); + assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b)); + + Bubble b2 = mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2); + + mBubbleController.promoteBubbleFromOverflow(b); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b); + } + + @Test public void testRemoveBubble_withDismissedNotif() { mEntryListener.onPendingEntryAdded(mRow.getEntry()); mBubbleController.updateBubble(mRow.getEntry()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index d5a654dc2b6f..eb4d438600d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -307,6 +307,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, controls.size) val controlStatus = controls[0] assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId) + assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure) assertTrue(controlStatus.favorite) assertTrue(controlStatus.removed) @@ -337,6 +338,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, controls.size) val controlStatus = controls[0] assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId) + assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure) assertTrue(controlStatus.favorite) assertFalse(controlStatus.removed) diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 00d0d9c428ff..6af5fe54f281 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -126,17 +126,17 @@ droidstubs { java_library { name: "framework-tethering-stubs-publicapi", srcs: [":framework-tethering-stubs-srcs-publicapi"], - sdk_version: "current", + defaults: ["framework-module-stubs-lib-defaults-publicapi"], } java_library { name: "framework-tethering-stubs-systemapi", srcs: [":framework-tethering-stubs-srcs-systemapi"], - sdk_version: "system_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } java_library { name: "framework-tethering-stubs-module_libs_api", srcs: [":framework-tethering-stubs-srcs-module_libs_api"], - sdk_version: "module_current", + defaults: ["framework-module-stubs-lib-defaults-systemapi"], } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 36113acf97d3..c84892d675f9 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -303,7 +303,8 @@ public class Tethering { final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); - mTetheringRestriction = new UserRestrictionActionListener(userManager, this); + mTetheringRestriction = new UserRestrictionActionListener( + userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); @@ -369,9 +370,10 @@ public class Tethering { mActiveDataSubId = subId; updateConfiguration(); + mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId); // To avoid launching unexpected provisioning checks, ignore re-provisioning // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() - // ill be triggered again when CarrierConfig is loaded. + // will be triggered again when CarrierConfig is loaded. if (mEntitlementMgr.getCarrierConfig(mConfig) != null) { mEntitlementMgr.reevaluateSimCardProvisioning(mConfig); } else { @@ -431,9 +433,7 @@ public class Tethering { // Called by wifi when the number of soft AP clients changed. @Override public void onConnectedClientsChanged(final List<WifiClient> clients) { - if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } + updateConnectedClients(clients); } } @@ -635,7 +635,10 @@ public class Tethering { Context.ETHERNET_SERVICE); synchronized (mPublicSync) { if (enable) { - if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR; + if (mEthernetCallback != null) { + Log.d(TAG, "Ethernet tethering already started"); + return TETHER_ERROR_NO_ERROR; + } mEthernetCallback = new EthernetCallback(); mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback); @@ -996,11 +999,14 @@ public class Tethering { protected static class UserRestrictionActionListener { private final UserManager mUserManager; private final Tethering mWrapper; + private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; - public UserRestrictionActionListener(UserManager um, Tethering wrapper) { + public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, + @NonNull TetheringNotificationUpdater updater) { mUserManager = um; mWrapper = wrapper; + mNotificationUpdater = updater; mDisallowTethering = false; } @@ -1019,13 +1025,21 @@ public class Tethering { return; } - // TODO: Add user restrictions notification. - final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); - - if (newlyDisallowed && isTetheringActiveOnDevice) { - mWrapper.untetherAll(); - // TODO(b/148139325): send tetheringSupported on restriction change + if (!newlyDisallowed) { + // Clear the restricted notification when user is allowed to have tethering + // function. + mNotificationUpdater.tetheringRestrictionLifted(); + return; } + + // Restricted notification is shown when tethering function is disallowed on + // user's device. + mNotificationUpdater.notifyTetheringDisabledByRestriction(); + + // Untether from all downstreams since tethering is disallowed. + mWrapper.untetherAll(); + + // TODO(b/148139325): send tetheringSupported on restriction change } } @@ -1559,6 +1573,7 @@ public class Tethering { mIPv6TetheringCoordinator.removeActiveDownstream(who); mOffload.excludeDownstreamInterface(who.interfaceName()); mForwardedDownstreams.remove(who); + updateConnectedClients(null /* wifiClients */); // If this is a Wi-Fi interface, tell WifiManager of any errors // or the inactive serving state. @@ -2141,6 +2156,12 @@ public class Tethering { return false; } + private void updateConnectedClients(final List<WifiClient> wifiClients) { + if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) { + reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); + } + } + private IpServer.Callback makeControlCallback() { return new IpServer.Callback() { @Override @@ -2155,10 +2176,7 @@ public class Tethering { @Override public void dhcpLeasesChanged() { - if (mConnectedClientsTracker.updateConnectedClients( - mForwardedDownstreams, null /* wifiClients */)) { - reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients()); - } + updateConnectedClients(null /* wifiClients */); } }; } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java index b97f75268a3b..992cdd8de6a7 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java @@ -29,12 +29,14 @@ import android.content.Intent; import android.content.res.Resources; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; +import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; @@ -54,10 +56,15 @@ import com.android.networkstack.tethering.R; public class TetheringNotificationUpdater { private static final String TAG = TetheringNotificationUpdater.class.getSimpleName(); private static final String CHANNEL_ID = "TETHERING_STATUS"; + private static final String WIFI_DOWNSTREAM = "WIFI"; + private static final String USB_DOWNSTREAM = "USB"; + private static final String BLUETOOTH_DOWNSTREAM = "BT"; private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; // Id to update and cancel tethering notification. Must be unique within the tethering app. - private static final int NOTIFY_ID = 20191115; + private static final int ENABLE_NOTIFICATION_ID = 1000; + // Id to update and cancel restricted notification. Must be unique within the tethering app. + private static final int RESTRICTED_NOTIFICATION_ID = 1001; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting @@ -65,14 +72,25 @@ public class TetheringNotificationUpdater { private final Context mContext; private final NotificationManager mNotificationManager; private final NotificationChannel mChannel; - // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. - // This value has to be made 1 2 and 4, and OR'd with the others. + // WARNING : the constructor is called on a different thread. Thread safety therefore // relies on this value being initialized to 0, and not any other value. If you need // to change this, you will need to change the thread where the constructor is invoked, // or to introduce synchronization. + // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2. + // This value has to be made 1 2 and 4, and OR'd with the others. private int mDownstreamTypesMask = DOWNSTREAM_NONE; + // WARNING : this value is not able to being initialized to 0 and must have volatile because + // telephony service is not guaranteed that is up before tethering service starts. If telephony + // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid + // subscription id(0) to query resources. Therefore, initialized subscription id must be + // INVALID_SUBSCRIPTION_ID. + private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + + @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) + @interface NotificationId {} + public TetheringNotificationUpdater(@NonNull final Context context) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) @@ -88,19 +106,46 @@ public class TetheringNotificationUpdater { public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; - updateNotification(); + updateEnableNotification(); + } + + /** Called when active data subscription id changed */ + public void onActiveDataSubscriptionIdChanged(final int subId) { + if (mActiveDataSubId == subId) return; + mActiveDataSubId = subId; + updateEnableNotification(); } - private void updateNotification() { + @VisibleForTesting + Resources getResourcesForSubId(@NonNull final Context c, final int subId) { + return SubscriptionManager.getResourcesForSubId(c, subId); + } + + private void updateEnableNotification() { final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { - clearNotification(); + clearNotification(ENABLE_NOTIFICATION_ID); } } - private void clearNotification() { - mNotificationManager.cancel(null /* tag */, NOTIFY_ID); + @VisibleForTesting + void tetheringRestrictionLifted() { + clearNotification(RESTRICTED_NOTIFICATION_ID); + } + + private void clearNotification(@NotificationId final int id) { + mNotificationManager.cancel(null /* tag */, id); + } + + @VisibleForTesting + void notifyTetheringDisabledByRestriction() { + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final String title = res.getString(R.string.disable_tether_notification_title); + final String message = res.getString(R.string.disable_tether_notification_message); + + showNotification(R.drawable.stat_sys_tether_general, title, message, + RESTRICTED_NOTIFICATION_ID); } /** @@ -110,16 +155,17 @@ public class TetheringNotificationUpdater { * * @return downstream types mask value. */ + @VisibleForTesting @IntRange(from = 0, to = 7) - private int getDownstreamTypesMask(@NonNull final String types) { + int getDownstreamTypesMask(@NonNull final String types) { int downstreamTypesMask = DOWNSTREAM_NONE; final String[] downstreams = types.split("\\|"); for (String downstream : downstreams) { - if ("USB".equals(downstream.trim())) { + if (USB_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_USB); - } else if ("WIFI".equals(downstream.trim())) { + } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if ("BT".equals(downstream.trim())) { + } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) { downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); } } @@ -134,9 +180,8 @@ public class TetheringNotificationUpdater { * * @return {@link android.util.SparseArray} with downstream types and icon id info. */ - @NonNull - private SparseArray<Integer> getIcons(@ArrayRes int id) { - final Resources res = mContext.getResources(); + @VisibleForTesting + SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) { final String[] array = res.getStringArray(id); final SparseArray<Integer> icons = new SparseArray<>(); for (String config : array) { @@ -161,8 +206,9 @@ public class TetheringNotificationUpdater { } private boolean setupNotification() { - final Resources res = mContext.getResources(); - final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons); + final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); + final SparseArray<Integer> downstreamIcons = + getIcons(R.array.tethering_notification_icons, res); final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID); if (iconId == NO_ICON_ID) return NO_NOTIFY; @@ -170,12 +216,12 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); - showNotification(iconId, title, message); + showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message) { + @NonNull final String message, @NotificationId final int id) { final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0), @@ -193,6 +239,6 @@ public class TetheringNotificationUpdater { .setContentIntent(pi) .build(); - mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); + mNotificationManager.notify(null /* tag */, id, notification); } } diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt new file mode 100644 index 000000000000..b86949185c69 --- /dev/null +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering + +import android.app.Notification +import android.app.NotificationManager +import android.content.Context +import android.content.res.Resources +import android.net.ConnectivityManager.TETHERING_BLUETOOTH +import android.net.ConnectivityManager.TETHERING_USB +import android.net.ConnectivityManager.TETHERING_WIFI +import android.os.UserHandle +import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.util.test.BroadcastInterceptingContext +import com.android.networkstack.tethering.R +import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mock +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.times +import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +const val TEST_SUBID = 1 +const val WIFI_ICON_ID = 1 +const val USB_ICON_ID = 2 +const val BT_ICON_ID = 3 +const val GENERAL_ICON_ID = 4 +const val WIFI_MASK = 1 shl TETHERING_WIFI +const val USB_MASK = 1 shl TETHERING_USB +const val BT_MASK = 1 shl TETHERING_BLUETOOTH +const val TITTLE = "Tethering active" +const val MESSAGE = "Tap here to set up." +const val TEST_TITTLE = "Hotspot active" +const val TEST_MESSAGE = "Tap to set up hotspot." + +@RunWith(AndroidJUnit4::class) +@SmallTest +class TetheringNotificationUpdaterTest { + // lateinit used here for mocks as they need to be reinitialized between each test and the test + // should crash if they are used before being initialized. + @Mock private lateinit var mockContext: Context + @Mock private lateinit var notificationManager: NotificationManager + @Mock private lateinit var defaultResources: Resources + @Mock private lateinit var testResources: Resources + + // lateinit for this class under test, as it should be reset to a different instance for every + // tests but should always be initialized before use (or the test should crash). + private lateinit var notificationUpdater: TetheringNotificationUpdater + + private val ENABLE_ICON_CONFIGS = arrayOf( + "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", + "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", + "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") + + private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) { + override fun createContextAsUser(user: UserHandle, flags: Int) = + if (user == UserHandle.ALL) mockContext else this + } + + private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) { + override fun getResourcesForSubId(context: Context, subId: Int) = + if (subId == TEST_SUBID) testResources else defaultResources + } + + private fun setupResources() { + doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources) + .getStringArray(R.array.tethering_notification_icons) + doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) + .getStringArray(R.array.tethering_notification_icons) + doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title) + doReturn(MESSAGE).`when`(defaultResources) + .getString(R.string.tethering_notification_message) + doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title) + doReturn(TEST_MESSAGE).`when`(testResources) + .getString(R.string.tethering_notification_message) + doReturn(USB_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/usb"), any(), any()) + doReturn(BT_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any()) + doReturn(GENERAL_ICON_ID).`when`(defaultResources) + .getIdentifier(eq("android.test:drawable/general"), any(), any()) + doReturn(WIFI_ICON_ID).`when`(testResources) + .getIdentifier(eq("android.test:drawable/wifi"), any(), any()) + } + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + val context = TestContext(InstrumentationRegistry.getInstrumentation().context) + doReturn(notificationManager).`when`(mockContext) + .getSystemService(Context.NOTIFICATION_SERVICE) + notificationUpdater = WrappedNotificationUpdater(context) + setupResources() + } + + private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE) + private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT) + + private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") { + verify(notificationManager, never()).cancel(any(), anyInt()) + + val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) + verify(notificationManager, times(1)) + .notify(any(), anyInt(), notificationCaptor.capture()) + + val notification = notificationCaptor.getValue() + assertEquals(iconId, notification.smallIcon.resId) + assertEquals(title, notification.title()) + assertEquals(text, notification.text()) + + reset(notificationManager) + } + + private fun verifyNoNotification() { + verify(notificationManager, times(1)).cancel(any(), anyInt()) + verify(notificationManager, never()).notify(any(), anyInt(), any()) + + reset(notificationManager) + } + + @Test + fun testNotificationWithDownstreamChanged() { + // Wifi downstream. No notification. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNoNotification() + + // Same downstream changed. Nothing happened. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyZeroInteractions(notificationManager) + + // Wifi and usb downstreams. Show enable notification + notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) + verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE) + + // Usb downstream. Still show enable notification. + notificationUpdater.onDownstreamChanged(USB_MASK) + verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNoNotification() + } + + @Test + fun testNotificationWithActiveDataSubscriptionIdChanged() { + // Usb downstream. Showed enable notification with default resource. + notificationUpdater.onDownstreamChanged(USB_MASK) + verifyNotification(USB_ICON_ID, TITTLE, MESSAGE) + + // Same subId changed. Nothing happened. + notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) + verifyZeroInteractions(notificationManager) + + // Set test sub id. Clear notification with test resource. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNoNotification() + + // Wifi downstream. Show enable notification with test resource. + notificationUpdater.onDownstreamChanged(WIFI_MASK) + verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE) + + // No downstream. No notification. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyNoNotification() + } + + private fun assertIconNumbers(number: Int, configs: Array<String?>) { + doReturn(configs).`when`(defaultResources) + .getStringArray(R.array.tethering_notification_icons) + assertEquals(number, notificationUpdater.getIcons( + R.array.tethering_notification_icons, defaultResources).size()) + } + + @Test + fun testGetIcons() { + assertIconNumbers(0, arrayOfNulls<String>(0)) + assertIconNumbers(0, arrayOf(null, "")) + assertIconNumbers(3, arrayOf( + // These configurations are invalid with wrong strings or symbols. + ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT", + "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test", + // This configuration is valid with two downstream types (USB, BT). + "USB|,,,,,|BT;drawable/test ", + // This configuration is valid with one downstream types (WIFI). + " WIFI ; android.test:drawable/xxx ")) + } + + @Test + fun testGetDownstreamTypesMask() { + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P")) + assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb")) + assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI ")) + assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T")) + assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT")) + assertEquals(WIFI_MASK or USB_MASK, + notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) + } + + @Test + fun testSetupRestrictedNotification() { + val title = InstrumentationRegistry.getInstrumentation().context.resources + .getString(R.string.disable_tether_notification_title) + val message = InstrumentationRegistry.getInstrumentation().context.resources + .getString(R.string.disable_tether_notification_message) + val disallowTitle = "Tether function is disallowed" + val disallowMessage = "Please contact your admin" + doReturn(title).`when`(defaultResources) + .getString(R.string.disable_tether_notification_title) + doReturn(message).`when`(defaultResources) + .getString(R.string.disable_tether_notification_message) + doReturn(disallowTitle).`when`(testResources) + .getString(R.string.disable_tether_notification_title) + doReturn(disallowMessage).`when`(testResources) + .getString(R.string.disable_tether_notification_message) + + // User restrictions on. Show restricted notification. + notificationUpdater.notifyTetheringDisabledByRestriction() + verifyNotification(R.drawable.stat_sys_tether_general, title, message) + + // User restrictions off. Clear notification. + notificationUpdater.tetheringRestrictionLifted() + verifyNoNotification() + + // Set test sub id. No notification. + notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) + verifyNoNotification() + + // User restrictions on again. Show restricted notification with test resource. + notificationUpdater.notifyTetheringDisabledByRestriction() + verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) + } +}
\ No newline at end of file diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 60d7ad1c5b1e..5ead1106d75a 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -210,7 +210,6 @@ public class TetheringTest { private PhoneStateListener mPhoneStateListener; private InterfaceConfigurationParcel mInterfaceConfiguration; - private class TestContext extends BroadcastInterceptingContext { TestContext(Context base) { super(base); @@ -1073,13 +1072,15 @@ public class TetheringTest { when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = - new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); + new Tethering.UserRestrictionActionListener( + mUserManager, mockTethering, mNotificationUpdater); ural.mDisallowTethering = currentDisallow; ural.onUserRestrictionsChanged(); - verify(mockTethering, times(expectedInteractionsWithShowNotification)) - .untetherAll(); + verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) + .notifyTetheringDisabledByRestriction(); + verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); } @Test @@ -1087,7 +1088,7 @@ public class TetheringTest { final String[] emptyActiveIfacesList = new String[]{}; final boolean currDisallow = false; final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 0; + final int expectedInteractionsWithShowNotification = 1; runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, expectedInteractionsWithShowNotification); @@ -1399,6 +1400,7 @@ public class TetheringTest { mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId); final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration(); assertEquals(fakeSubId, newConfig.activeDataSubId); + verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId)); } @Test diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java index 6112da5cd13b..fe9f60fe2859 100644 --- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java +++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java @@ -37,7 +37,6 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; -import android.util.Size; import android.view.Display; import android.view.View; import android.widget.Toast; @@ -358,8 +357,8 @@ public class WallpaperCropActivity extends Activity { // Get the crop boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - Size windowSize = getWindowManager().getCurrentWindowMetrics().getSize(); - boolean isPortrait = windowSize.getWidth() < windowSize.getHeight(); + Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds(); + boolean isPortrait = windowBounds.width() < windowBounds.height(); Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(), getDisplay()); diff --git a/services/Android.bp b/services/Android.bp index 490481c7e5a2..52c5993a2862 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -142,6 +142,11 @@ droidstubs { baseline_file: "api/lint-baseline.txt", }, }, + dist: { + targets: ["sdk", "win_sdk"], + dir: "apistubs/android/system-server/api", + dest: "android.txt", + }, } java_library { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 60c3d7859984..1b180e3357d7 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -2001,17 +2001,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId, currentTargets, str -> str); scheduleNotifyClientsOfServicesStateChangeLocked(userState); - - // Disable accessibility shortcut key if there's no shortcut installed. - if (currentTargets.isEmpty()) { - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } } private boolean canRequestAndRequestsTouchExplorationLocked( diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index c39e93a84b48..2306329e689c 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -165,7 +165,8 @@ public final class InlineSuggestionFactory { Slog.w(TAG, "InlinePresentation not found in dataset"); continue; } - if (!includeDataset(dataset, fieldIndex, filterText)) { + if (!inlinePresentation.isPinned() // don't filter pinned suggestions + && !includeDataset(dataset, fieldIndex, filterText)) { continue; } InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset, diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index f1f5005b23db..8dd4fa6d8fd1 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -1101,6 +1101,9 @@ public final class BatteryService extends SystemService { * Synchronize on BatteryService. */ public void updateLightsLocked() { + if (mBatteryLight == null) { + return; + } final int level = mHealthInfo.batteryLevel; final int status = mHealthInfo.batteryStatus; if (level < mLowBatteryWarningLevel) { diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 03ca1c610f82..1bf559a17021 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -112,9 +112,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int USER_SWITCHED_TIME_MS = 200; // Delay for the addProxy function in msec private static final int ADD_PROXY_DELAY_MS = 100; + // Delay for retrying enable and disable in msec + private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; + private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; + private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; private static final int MESSAGE_REGISTER_ADAPTER = 20; private static final int MESSAGE_UNREGISTER_ADAPTER = 21; private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30; @@ -136,6 +140,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private static final int RESTORE_SETTING_TO_OFF = 0; private static final int MAX_ERROR_RESTART_RETRIES = 6; + private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10; // Bluetooth persisted setting is off private static final int BLUETOOTH_OFF = 0; @@ -166,6 +171,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + private int mWaitForEnableRetry; + private int mWaitForDisableRetry; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; @@ -1678,8 +1685,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub { break; case MESSAGE_ENABLE: + int quietEnable = msg.arg1; + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE, + quietEnable, 0), ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = " + + mBluetooth); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; @@ -1702,7 +1719,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } - mQuietEnable = (msg.arg1 == 1); + mQuietEnable = (quietEnable == 1); if (mBluetooth == null) { handleEnable(mQuietEnable); } else { @@ -1711,8 +1728,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // the previous Bluetooth process has exited. The // waiting period has three components: // (a) Wait until the local state is STATE_OFF. This - // is accomplished by - // "waitForState(Set.of(BluetoothAdapter.STATE_OFF))". + // is accomplished by sending delay a message + // MESSAGE_HANDLE_ENABLE_DELAYED // (b) Wait until the STATE_OFF state is updated to // all components. // (c) Wait until the Bluetooth process exits, and @@ -1722,33 +1739,108 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // message. The delay time is backed off if Bluetooth // continuously failed to turn on itself. // - waitForState(Set.of(BluetoothAdapter.STATE_OFF)); - Message restartMsg = - mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); - mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + mWaitForEnableRetry = 0; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); } break; case MESSAGE_DISABLE: + if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + // We are handling enable or disable right now, wait for it. + mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE), + ENABLE_DISABLE_DELAY_MS); + break; + } + if (DBG) { - Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth); + Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth + + ", mBinding = " + mBinding); } mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); + if (mEnable && mBluetooth != null) { - waitForState(Set.of(BluetoothAdapter.STATE_ON)); + mWaitForDisableRetry = 0; + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { mEnable = false; handleDisable(); - waitForState(Set.of(BluetoothAdapter.STATE_OFF, - BluetoothAdapter.STATE_TURNING_ON, - BluetoothAdapter.STATE_TURNING_OFF, - BluetoothAdapter.STATE_BLE_TURNING_ON, - BluetoothAdapter.STATE_BLE_ON, - BluetoothAdapter.STATE_BLE_TURNING_OFF)); - } else { + } + break; + + case MESSAGE_HANDLE_ENABLE_DELAYED: { + // The Bluetooth is turning off, wait for STATE_OFF + if (mState != BluetoothAdapter.STATE_OFF) { + if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForEnableRetry++; + Message enableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED); + mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_OFF timeout"); + } + } + // Either state is changed to STATE_OFF or reaches the maximum retry, we + // should move forward to the next step. + mWaitForEnableRetry = 0; + Message restartMsg = + mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE); + mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs()); + Slog.d(TAG, "Handle enable is finished"); + break; + } + + case MESSAGE_HANDLE_DISABLE_DELAYED: { + boolean disabling = (msg.arg1 == 1); + Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling); + if (!disabling) { + // The Bluetooth is turning on, wait for STATE_ON + if (mState != BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for STATE_ON timeout"); + } + } + // Either state is changed to STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + mWaitForDisableRetry = 0; mEnable = false; handleDisable(); + // Wait for state exiting STATE_ON + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } else { + // The Bluetooth is turning off, wait for exiting STATE_ON + if (mState == BluetoothAdapter.STATE_ON) { + if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) { + mWaitForDisableRetry++; + Message disableDelayedMsg = mHandler.obtainMessage( + MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, + ENABLE_DISABLE_DELAY_MS); + break; + } else { + Slog.e(TAG, "Wait for exiting STATE_ON timeout"); + } + } + // Either state is exited from STATE_ON or reaches the maximum retry, we + // should move forward to the next step. + Slog.d(TAG, "Handle disable is finished"); } break; + } case MESSAGE_RESTORE_USER_SETTING: if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) { @@ -2124,6 +2216,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { try { mBluetoothLock.writeLock().lock(); if ((mBluetooth == null) && (!mBinding)) { + Slog.d(TAG, "binding Bluetooth service"); //Start bind timeout and bind Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND); mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS); @@ -2493,6 +2586,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" " + app.getPackageName()); } + writer.println("\nBluetoothManagerService:"); + writer.println(" mEnable:" + mEnable); + writer.println(" mQuietEnable:" + mQuietEnable); + writer.println(" mEnableExternal:" + mEnableExternal); + writer.println(" mQuietEnableExternal:" + mQuietEnableExternal); + writer.println(""); writer.flush(); if (args.length == 0) { diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 1bb3c3ac0cda..0ddfa1c16a0a 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -886,6 +886,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { @Override public void setIPv6AddrGenMode(String iface, int mode) throws ServiceSpecificException { + NetworkStack.checkNetworkStackPermission(mContext); try { mNetdService.setIPv6AddrGenMode(iface, mode); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index bb9446078da6..4d8c86c87e9e 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -43,6 +43,7 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIE import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; import static android.os.storage.StorageManager.PROP_FUSE; +import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY; import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -903,6 +904,7 @@ class StorageManagerService extends IStorageManager.Stub refreshIsolatedStorageSettings(); } }); + updateLegacyStorageOpSticky(); // For now, simply clone property when it changes DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, mContext.getMainExecutor(), (properties) -> { @@ -1779,6 +1781,13 @@ class StorageManagerService extends IStorageManager.Stub } } + private void updateLegacyStorageOpSticky() { + final boolean propertyValue = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + "legacy_storage_op_sticky", true); + SystemProperties.set(PROP_LEGACY_OP_STICKY, propertyValue ? "true" : "false"); + } + private void start() { connectStoraged(); connectVold(); @@ -4450,9 +4459,8 @@ class StorageManagerService extends IStorageManager.Stub String.format("/storage/emulated/%d/Android/data/%s/", userId, pkg); - int appUid = - UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid()); // Create package obb and data dir if it doesn't exist. + int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid()); File file = new File(packageObbDir); if (!file.exists()) { vold.setupAppDir(packageObbDir, appUid); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4cfcd2b218d1..38405e1ccc03 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -185,8 +185,6 @@ import android.app.PendingIntent; import android.app.ProcessMemoryState; import android.app.ProfilerInfo; import android.app.WaitResult; -import android.app.WindowConfiguration.ActivityType; -import android.app.WindowConfiguration.WindowingMode; import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; @@ -6513,13 +6511,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType, - @WindowingMode int ignoreWindowingMode) { - return mActivityTaskManager.getFilteredTasks( - maxNum, ignoreActivityType, ignoreWindowingMode); - } - - @Override public void cancelTaskWindowTransition(int taskId) { mActivityTaskManager.cancelTaskWindowTransition(taskId); } @@ -8780,6 +8771,27 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean isUidActiveOrForeground(int uid, String callingPackage) { + if (!hasUsageStatsPermission(callingPackage)) { + enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS, + "isUidActiveOrForeground"); + } + synchronized (this) { + final boolean isActive = isUidActiveLocked(uid); + if (isActive) { + return true; + } + } + final boolean isForeground = mAtmInternal.isUidForeground(uid); + if (isForeground) { + Slog.wtf(TAG, "isUidActiveOrForeground: isUidActive false but " + + " isUidForeground true, uid:" + uid + + " callingPackage:" + callingPackage); + } + return isForeground; + } + + @Override public void setPersistentVrThread(int tid) { mActivityTaskManager.setPersistentVrThread(tid); } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4d08bd2e0ed7..bee0e055cf3f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -98,6 +98,7 @@ import android.provider.DeviceConfig; import android.system.Os; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.EventLog; import android.util.LongSparseArray; import android.util.Pair; @@ -138,6 +139,7 @@ import java.util.BitSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Activity manager code dealing with processes. @@ -2155,15 +2157,6 @@ public final class ProcessList { result.put(packageName, Pair.create(volumeUuid, inode)); } } - if (mAppDataIsolationWhitelistedApps != null) { - for (String packageName : mAppDataIsolationWhitelistedApps) { - String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid(); - long inode = pmInt.getCeDataInode(packageName, userId); - if (inode != 0) { - result.put(packageName, Pair.create(volumeUuid, inode)); - } - } - } return result; } @@ -2184,34 +2177,42 @@ public final class ProcessList { app.setHasForegroundActivities(true); } - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); final Map<String, Pair<String, Long>> pkgDataInfoMap; + final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap; boolean bindMountAppStorageDirs = false; - - if (shouldIsolateAppData(app)) { - // Get all packages belongs to the same shared uid. sharedPackages is empty array - // if it doesn't have shared uid. - final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked(); - final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage( - app.info.packageName, app.userId); - pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0 - ? new String[]{app.info.packageName} : sharedPackages, uid); - - int userId = UserHandle.getUserId(uid); - if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) && - !storageManagerInternal.isExternalStorageService(uid)) { - bindMountAppStorageDirs = true; - if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), - app.processName)) { - // Cannot prepare Android/app and Android/obb directory, - // so we won't mount it in zygote. - app.bindMountPending = true; - bindMountAppStorageDirs = false; - } + boolean bindMountAppsData = shouldIsolateAppData(app); + + // Get all packages belongs to the same shared uid. sharedPackages is empty array + // if it doesn't have shared uid. + final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked(); + final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage( + app.info.packageName, app.userId); + final String[] targetPackagesList = sharedPackages.length == 0 + ? new String[]{app.info.packageName} : sharedPackages; + pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid); + + // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so + // it won't be mounted twice. + final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps); + for (String pkg : targetPackagesList) { + whitelistedApps.remove(pkg); + } + whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt, + whitelistedApps.toArray(new String[0]), uid); + + int userId = UserHandle.getUserId(uid); + StorageManagerInternal storageManagerInternal = LocalServices.getService( + StorageManagerInternal.class); + if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) && + !storageManagerInternal.isExternalStorageService(uid)) { + bindMountAppStorageDirs = true; + if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), + app.processName)) { + // Cannot prepare Android/app and Android/obb directory, + // so we won't mount it in zygote. + app.bindMountPending = true; + bindMountAppStorageDirs = false; } - } else { - pkgDataInfoMap = null; } final Process.ProcessStartResult startResult; @@ -2229,7 +2230,8 @@ public final class ProcessList { app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp, - app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs, + app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap, + bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.startSeq}); } else { startResult = Process.start(entryPoint, @@ -2237,7 +2239,7 @@ public final class ProcessList { app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags, isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap, - bindMountAppStorageDirs, + whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, new String[]{PROC_START_SEQ_IDENT + app.startSeq}); } checkSlow(startTime, "startProcess: returned from zygote!"); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index ada3e4269f6a..94675ab7d795 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -776,6 +776,8 @@ public class AudioService extends IAudioService.Stub mDeviceBroker = new AudioDeviceBroker(mContext, this); + mRecordMonitor = new RecordingActivityMonitor(mContext); + // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[] // array initialized by updateStreamVolumeAlias() updateStreamVolumeAlias(false /*updateVolumes*/, TAG); @@ -797,8 +799,6 @@ public class AudioService extends IAudioService.Stub mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); - mRecordMonitor = new RecordingActivityMonitor(mContext); - readAndSetLowRamDevice(); mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported(); @@ -1981,8 +1981,7 @@ public class AudioService extends IAudioService.Stub } flags &= ~AudioManager.FLAG_FIXED_VOLUME; - if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) && - mFixedVolumeDevices.contains(device)) { + if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) { flags |= AudioManager.FLAG_FIXED_VOLUME; // Always toggle between max safe volume and 0 for fixed volume devices where safe @@ -2059,7 +2058,7 @@ public class AudioService extends IAudioService.Stub !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex); mVolumeController.postDisplaySafeVolumeWarning(flags); - } else if (!mFullVolumeDevices.contains(device) + } else if (!isFullVolumeDevice(device) && (streamState.adjustIndex(direction * step, device, caller) || streamState.mIsMuted)) { // Post message to set system volume (it in turn will post a @@ -2121,7 +2120,7 @@ public class AudioService extends IAudioService.Stub if (mHdmiCecSink && streamTypeAlias == AudioSystem.STREAM_MUSIC // vol change on a full volume device - && mFullVolumeDevices.contains(device)) { + && isFullVolumeDevice(device)) { int keyCode = KeyEvent.KEYCODE_UNKNOWN; switch (direction) { case AudioManager.ADJUST_RAISE: @@ -2325,6 +2324,13 @@ public class AudioService extends IAudioService.Stub // For legacy reason, propagate to all streams associated to this volume group for (final int groupedStream : vgs.getLegacyStreamTypes()) { + try { + ensureValidStreamType(groupedStream); + } catch (IllegalArgumentException e) { + Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream + + "), do not change associated stream volume"); + continue; + } setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage, Binder.getCallingUid()); } @@ -2590,8 +2596,7 @@ public class AudioService extends IAudioService.Stub } flags &= ~AudioManager.FLAG_FIXED_VOLUME; - if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) && - mFixedVolumeDevices.contains(device)) { + if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) { flags |= AudioManager.FLAG_FIXED_VOLUME; // volume is either 0 or max allowed for fixed volume devices @@ -2780,7 +2785,7 @@ public class AudioService extends IAudioService.Stub if (streamType == AudioSystem.STREAM_MUSIC) { flags = updateFlagsForTvPlatform(flags); - if (mFullVolumeDevices.contains(device)) { + if (isFullVolumeDevice(device)) { flags &= ~AudioManager.FLAG_SHOW_UI; } } @@ -2826,7 +2831,7 @@ public class AudioService extends IAudioService.Stub int device, boolean force, String caller) { - if (mFullVolumeDevices.contains(device)) { + if (isFullVolumeDevice(device)) { return; } VolumeStreamState streamState = mStreamStates[streamType]; @@ -3036,7 +3041,7 @@ public class AudioService extends IAudioService.Stub index = 0; } if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) && - mFixedVolumeDevices.contains(device)) { + isFixedVolumeDevice(device)) { index = mStreamStates[streamType].getMaxIndex(); } return (index + 5) / 10; @@ -4883,10 +4888,6 @@ public class AudioService extends IAudioService.Stub public void applyAllVolumes() { synchronized (VolumeGroupState.class) { - if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) { - // No-op to avoid regression with stream based volume management - return; - } // apply device specific volumes first int index; for (int i = 0; i < mIndexMap.size(); i++) { @@ -5166,7 +5167,7 @@ public class AudioService extends IAudioService.Stub } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) && isAvrcpAbsVolSupported) { index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); - } else if (mFullVolumeDevices.contains(device)) { + } else if (isFullVolumeDevice(device)) { index = (mIndexMax + 5)/10; } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { index = (mIndexMax + 5)/10; @@ -5189,7 +5190,7 @@ public class AudioService extends IAudioService.Stub } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) && isAvrcpAbsVolSupported) { index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); - } else if (mFullVolumeDevices.contains(device)) { + } else if (isFullVolumeDevice(device)) { index = (mIndexMax + 5)/10; } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { index = (mIndexMax + 5)/10; @@ -5390,8 +5391,8 @@ public class AudioService extends IAudioService.Stub for (int i = 0; i < mIndexMap.size(); i++) { int device = mIndexMap.keyAt(i); int index = mIndexMap.valueAt(i); - if (mFullVolumeDevices.contains(device) - || (mFixedVolumeDevices.contains(device) && index != 0)) { + if (isFullVolumeDevice(device) + || (isFixedVolumeDevice(device) && index != 0)) { mIndexMap.put(device, mIndexMax); } applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported); @@ -8236,4 +8237,23 @@ public class AudioService extends IAudioService.Stub new HashMap<IBinder, AudioPolicyProxy>(); @GuardedBy("mAudioPolicies") private int mAudioPolicyCounter = 0; + + //====================== + // Helper functions for full and fixed volume device + //====================== + private boolean isFixedVolumeDevice(int deviceType) { + if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX + && mRecordMonitor.isLegacyRemoteSubmixActive()) { + return false; + } + return mFixedVolumeDevices.contains(deviceType); + } + + private boolean isFullVolumeDevice(int deviceType) { + if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX + && mRecordMonitor.isLegacyRemoteSubmixActive()) { + return false; + } + return mFullVolumeDevices.contains(deviceType); + } } diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java index 5c5096251e79..65f221899818 100644 --- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java +++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java @@ -18,6 +18,7 @@ package com.android.server.audio; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioDeviceInfo; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecordingConfiguration; @@ -35,6 +36,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; /** * Class to receive and dispatch updates from AudioSystem about recording configurations. @@ -49,6 +52,16 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin // playback configurations that do not contain uid/package name information. private boolean mHasPublicClients = false; + + // When legacy remote submix device is active, remote submix device should not be fixed and + // full volume device. When legacy remote submix device is active, there will be a recording + // activity using device with type as {@link AudioSystem.DEVICE_OUT_REMOTE_SUBMIX} and address + // as {@link AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS}. Cache riid of legacy remote submix + // since remote submix state is not cached in mRecordStates. + private AtomicInteger mLegacyRemoteSubmixRiid = + new AtomicInteger(AudioManager.RECORD_RIID_INVALID); + private AtomicBoolean mLegacyRemoteSubmixActive = new AtomicBoolean(false); + static final class RecordingState { private final int mRiid; private final RecorderDeathHandler mDeathHandler; @@ -137,6 +150,16 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin final AudioRecordingConfiguration config = createRecordingConfiguration( uid, session, source, recordingInfo, portId, silenced, activeSource, clientEffects, effects); + if (source == MediaRecorder.AudioSource.REMOTE_SUBMIX) { + final AudioDeviceInfo device = config.getAudioDevice(); + if (AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS.equals(device.getAddress())) { + mLegacyRemoteSubmixRiid.set(riid); + if (event == AudioManager.RECORD_CONFIG_EVENT_START + || event == AudioManager.RECORD_CONFIG_EVENT_UPDATE) { + mLegacyRemoteSubmixActive.set(true); + } + } + } if (MediaRecorder.isSystemOnlyAudioSource(source)) { // still want to log event, it just won't appear in recording configurations; sEventLogger.log(new RecordingEvent(event, riid, config).printLog(TAG)); @@ -170,6 +193,9 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin * Receive an event from the client about a tracked recorder */ public void recorderEvent(int riid, int event) { + if (mLegacyRemoteSubmixRiid.get() == riid) { + mLegacyRemoteSubmixActive.set(event == AudioManager.RECORDER_STATE_STARTED); + } int configEvent = event == AudioManager.RECORDER_STATE_STARTED ? AudioManager.RECORD_CONFIG_EVENT_START : event == AudioManager.RECORDER_STATE_STOPPED @@ -323,6 +349,13 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin } /** + * Return true if legacy remote submix device is active. Otherwise, return false. + */ + boolean isLegacyRemoteSubmixActive() { + return mLegacyRemoteSubmixActive.get(); + } + + /** * Create a recording configuration from the provided parameters * @param uid * @param session diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index d45ffd9918e4..ff8e3a973641 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -202,8 +202,7 @@ public class AuthService extends SystemService { // Only allow internal clients to call canAuthenticate with a different userId. final int callingUserId = UserHandle.getCallingUserId(); - Slog.d(TAG, "canAuthenticate, userId: " + userId + ", callingUserId: " + callingUserId - + ", authenticators: " + authenticators); + if (userId != callingUserId) { checkInternalPermission(); } else { @@ -212,8 +211,14 @@ public class AuthService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { - return mBiometricService.canAuthenticate( + final int result = mBiometricService.canAuthenticate( opPackageName, userId, callingUserId, authenticators); + Slog.d(TAG, "canAuthenticate" + + ", userId: " + userId + + ", callingUserId: " + callingUserId + + ", authenticators: " + authenticators + + ", result: " + result); + return result; } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 766e5c4d638f..5d334c22f2db 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -66,6 +66,8 @@ public abstract class AuthenticationClient extends ClientMonitor { public abstract boolean wasUserDetected(); + public abstract boolean isStrongBiometric(); + public AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, @@ -167,9 +169,15 @@ public abstract class AuthenticationClient extends ClientMonitor { } if (isBiometricPrompt() && listener != null) { // BiometricService will add the token to keystore - listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken); + listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken, + isStrongBiometric()); } else if (!isBiometricPrompt() && listener != null) { - KeyStore.getInstance().addAuthToken(byteToken); + if (isStrongBiometric()) { + KeyStore.getInstance().addAuthToken(byteToken); + } else { + Slog.d(getLogTag(), "Skipping addAuthToken"); + } + try { // Explicitly have if/else here to make it super obvious in case the code is // touched in the future. diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index e7c09baf3aeb..233416d663d9 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -266,7 +266,8 @@ public class BiometricService extends SystemService { SomeArgs args = (SomeArgs) msg.obj; handleAuthenticationSucceeded( (boolean) args.arg1 /* requireConfirmation */, - (byte[]) args.arg2 /* token */); + (byte[]) args.arg2 /* token */, + (boolean) args.arg3 /* isStrongBiometric */); args.recycle(); break; } @@ -568,10 +569,12 @@ public class BiometricService extends SystemService { final IBiometricServiceReceiverInternal mInternalReceiver = new IBiometricServiceReceiverInternal.Stub() { @Override - public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) { + public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) { SomeArgs args = SomeArgs.obtain(); args.arg1 = requireConfirmation; args.arg2 = token; + args.arg3 = isStrongBiometric; mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget(); } @@ -761,8 +764,13 @@ public class BiometricService extends SystemService { + " config_biometric_sensors?"); } + // Note that we allow BIOMETRIC_CONVENIENCE to register because BiometricService + // also does / will do other things such as keep track of lock screen timeout, etc. + // Just because a biometric is registered does not mean it can participate in + // the android.hardware.biometrics APIs. if (strength != Authenticators.BIOMETRIC_STRONG - && strength != Authenticators.BIOMETRIC_WEAK) { + && strength != Authenticators.BIOMETRIC_WEAK + && strength != Authenticators.BIOMETRIC_CONVENIENCE) { throw new IllegalStateException("Unsupported strength"); } @@ -1189,8 +1197,10 @@ public class BiometricService extends SystemService { BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL); } } else { + // This should not be possible via the public API surface and is here mainly for + // "correctness". An exception should have been thrown before getting here. Slog.e(TAG, "No authenticators requested"); - return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE); + return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT); } } @@ -1286,7 +1296,8 @@ public class BiometricService extends SystemService { return modality; } - private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) { + private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) { try { // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded // after user dismissed/canceled dialog). @@ -1295,9 +1306,16 @@ public class BiometricService extends SystemService { return; } - // Store the auth token and submit it to keystore after the dialog is confirmed / - // animating away. - mCurrentAuthSession.mTokenEscrow = token; + if (isStrongBiometric) { + // Store the auth token and submit it to keystore after the dialog is confirmed / + // animating away. + mCurrentAuthSession.mTokenEscrow = token; + } else { + if (token != null) { + Slog.w(TAG, "Dropping authToken for non-strong biometric"); + } + } + if (!requireConfirmation) { mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI; } else { diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index ebd407d0e981..45b93834c1e2 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -413,8 +413,8 @@ public abstract class BiometricServiceBase extends SystemService throw new UnsupportedOperationException("Stub!"); } - default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token) - throws RemoteException { + default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) throws RemoteException { throw new UnsupportedOperationException("Stub!"); } @@ -451,10 +451,11 @@ public abstract class BiometricServiceBase extends SystemService } @Override - public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token) - throws RemoteException { + public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token, + boolean isStrongBiometric) throws RemoteException { if (getWrapperReceiver() != null) { - getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token); + getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token, + isStrongBiometric); } } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index fd54129a3263..3ecf87c6860f 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -236,6 +236,11 @@ public class FaceService extends BiometricServiceBase { } @Override + public boolean isStrongBiometric() { + return FaceService.this.isStrongBiometric(); + } + + @Override public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { final boolean result = super.onAuthenticated(identifier, authenticated, token); diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index acb1a2ffb754..8520f5aa0632 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -161,6 +161,11 @@ public class FingerprintService extends BiometricServiceBase { } @Override + public boolean isStrongBiometric() { + return FingerprintService.this.isStrongBiometric(); + } + + @Override public int handleFailedAttempt() { final int currentUser = ActivityManager.getCurrentUser(); mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 05a757bde179..8eb771046e6d 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -646,7 +646,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { + "id=" + physicalDisplayId + ", state=" + Display.stateToString(state) + ")"); } - mBacklight.setVrMode(isVrEnabled); + if (mBacklight != null) { + mBacklight.setVrMode(isVrEnabled); + } } private void setDisplayState(int state) { @@ -708,7 +710,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { BrightnessSynchronizer.brightnessFloatToInt(getContext(), brightness)); } - mBacklight.setBrightness(brightness); + if (mBacklight != null) { + mBacklight.setBrightness(brightness); + } Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenBrightness", BrightnessSynchronizer.brightnessFloatToInt( diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0b22586bb373..e6129b9b1f32 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -76,7 +76,6 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; -import android.view.InputWindowHandle; import android.view.KeyEvent; import android.view.PointerIcon; import android.view.Surface; @@ -221,8 +220,7 @@ public class InputManagerService extends IInputManager.Stub int policyFlags); private static native VerifiedInputEvent nativeVerifyInputEvent(long ptr, InputEvent event); private static native void nativeToggleCapsLock(long ptr, int deviceId); - private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles, - int displayId); + private static native void nativeDisplayRemoved(long ptr, int displayId); private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen); private static native void nativeSetSystemUiVisibility(long ptr, int visibility); private static native void nativeSetFocusedApplication(long ptr, @@ -1536,7 +1534,7 @@ public class InputManagerService extends IInputManager.Stub /** Clean up input window handles of the given display. */ public void onDisplayRemoved(int displayId) { - nativeSetInputWindows(mPtr, null /* windowHandles */, displayId); + nativeDisplayRemoved(mPtr, displayId); } @Override diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java index 521913a0c439..706c74137755 100644 --- a/services/core/java/com/android/server/lights/LightsManager.java +++ b/services/core/java/com/android/server/lights/LightsManager.java @@ -16,6 +16,7 @@ package com.android.server.lights; +import android.annotation.Nullable; import android.hardware.light.V2_0.Type; public abstract class LightsManager { @@ -30,7 +31,8 @@ public abstract class LightsManager { public static final int LIGHT_ID_COUNT = Type.COUNT; /** - * Returns the logical light with the given type. + * Returns the logical light with the given type, if it exists, or null. */ + @Nullable public abstract LogicalLight getLight(int id); } diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index a42dec8b575f..3c6e8d29cae0 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -52,8 +52,8 @@ public class LightsService extends SystemService { static final String TAG = "LightsService"; static final boolean DEBUG = false; - private LightImpl[] mLights = null; - private SparseArray<LightImpl> mLightsById = null; + private final LightImpl[] mLightsByType = new LightImpl[LightsManager.LIGHT_ID_COUNT]; + private final SparseArray<LightImpl> mLightsById = new SparseArray<>(); private ILights mVintfLights = null; @@ -96,8 +96,8 @@ public class LightsService extends SystemService { synchronized (LightsService.this) { final List<Light> lights = new ArrayList<Light>(); for (int i = 0; i < mLightsById.size(); i++) { - HwLight hwLight = mLightsById.valueAt(i).getHwLight(); - if (!isSystemLight(hwLight)) { + if (!mLightsById.valueAt(i).isSystemLight()) { + HwLight hwLight = mLightsById.valueAt(i).mHwLight; lights.add(new Light(hwLight.id, hwLight.ordinal, hwLight.type)); } } @@ -138,7 +138,7 @@ public class LightsService extends SystemService { synchronized (LightsService.this) { final LightImpl light = mLightsById.get(lightId); - if (light == null || isSystemLight(light.getHwLight())) { + if (light == null || light.isSystemLight()) { throw new IllegalArgumentException("Invalid light: " + lightId); } return new LightState(light.getColor()); @@ -184,9 +184,8 @@ public class LightsService extends SystemService { private void checkRequestIsValid(int[] lightIds) { for (int i = 0; i < lightIds.length; i++) { final LightImpl light = mLightsById.get(lightIds[i]); - final HwLight hwLight = light.getHwLight(); - Preconditions.checkState(light != null && !isSystemLight(hwLight), - "invalid lightId " + hwLight.id); + Preconditions.checkState(light != null && !light.isSystemLight(), + "Invalid lightId " + lightIds[i]); } } @@ -205,9 +204,8 @@ public class LightsService extends SystemService { } for (int i = 0; i < mLightsById.size(); i++) { LightImpl light = mLightsById.valueAt(i); - HwLight hwLight = light.getHwLight(); - if (!isSystemLight(hwLight)) { - LightState state = states.get(hwLight.id); + if (!light.isSystemLight()) { + LightState state = states.get(light.mHwLight.id); if (state != null) { light.setColor(state.getColor()); } else { @@ -385,26 +383,22 @@ public class LightsService extends SystemService { int brightnessMode) { Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x" + Integer.toHexString(color) + ")"); - if (mVintfLights != null) { - HwLightState lightState = new HwLightState(); - lightState.color = color; - lightState.flashMode = (byte) mode; - lightState.flashOnMs = onMS; - lightState.flashOffMs = offMS; - lightState.brightnessMode = (byte) brightnessMode; - try { + try { + if (mVintfLights != null) { + HwLightState lightState = new HwLightState(); + lightState.color = color; + lightState.flashMode = (byte) mode; + lightState.flashOnMs = onMS; + lightState.flashOffMs = offMS; + lightState.brightnessMode = (byte) brightnessMode; mVintfLights.setLightState(mHwLight.id, lightState); - } catch (RemoteException | UnsupportedOperationException ex) { - Slog.e(TAG, "Failed issuing setLightState", ex); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); - } - } else { - try { + } else { setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_POWER); } + } catch (RemoteException | UnsupportedOperationException ex) { + Slog.e(TAG, "Failed issuing setLightState", ex); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); } } @@ -412,8 +406,14 @@ public class LightsService extends SystemService { return mVrModeEnabled && mUseLowPersistenceForVR; } - private HwLight getHwLight() { - return mHwLight; + /** + * Returns whether a light is system-use-only or should be accessible to + * applications using the {@link android.hardware.lights.LightsManager} API. + */ + private boolean isSystemLight() { + // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system lights. + // Newly-added lights are made available via the public LightsManager API. + return (0 <= mHwLight.type && mHwLight.type < LightsManager.LIGHT_ID_COUNT); } private int getColor() { @@ -451,36 +451,37 @@ public class LightsService extends SystemService { } private void populateAvailableLights(Context context) { - mLights = new LightImpl[LightsManager.LIGHT_ID_COUNT]; - mLightsById = new SparseArray<>(); - if (mVintfLights != null) { - try { - for (HwLight availableLight : mVintfLights.getLights()) { - LightImpl light = new LightImpl(context, availableLight); - int type = (int) availableLight.type; - if (0 <= type && type < mLights.length && mLights[type] == null) { - mLights[type] = light; - } - mLightsById.put(availableLight.id, light); - } - } catch (RemoteException ex) { - Slog.e(TAG, "Unable to get lights for initialization", ex); + populateAvailableLightsFromAidl(context); + } else { + populateAvailableLightsFromHidl(context); + } + + for (int i = mLightsById.size() - 1; i >= 0; i--) { + final int type = mLightsById.keyAt(i); + if (0 <= type && type < mLightsByType.length) { + mLightsByType[type] = mLightsById.valueAt(i); } } + } - // In the case where only the old HAL is available, all lights will be initialized here - for (int i = 0; i < mLights.length; i++) { - if (mLights[i] == null) { - // The ordinal can be anything if there is only 1 light of each type. Set it to 1. - HwLight light = new HwLight(); - light.id = (byte) i; - light.ordinal = 1; - light.type = (byte) i; - - mLights[i] = new LightImpl(context, light); - mLightsById.put(i, mLights[i]); + private void populateAvailableLightsFromAidl(Context context) { + try { + for (HwLight hwLight : mVintfLights.getLights()) { + mLightsById.put(hwLight.id, new LightImpl(context, hwLight)); } + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to get lights from HAL", ex); + } + } + + private void populateAvailableLightsFromHidl(Context context) { + for (int i = 0; i < mLightsByType.length; i++) { + HwLight hwLight = new HwLight(); + hwLight.id = (byte) i; + hwLight.ordinal = 1; + hwLight.type = (byte) i; + mLightsById.put(hwLight.id, new LightImpl(context, hwLight)); } } @@ -505,25 +506,14 @@ public class LightsService extends SystemService { private final LightsManager mService = new LightsManager() { @Override public LogicalLight getLight(int lightType) { - if (mLights != null && 0 <= lightType && lightType < mLights.length) { - return mLights[lightType]; + if (mLightsByType != null && 0 <= lightType && lightType < mLightsByType.length) { + return mLightsByType[lightType]; } else { return null; } } }; - /** - * Returns whether a light is system-use-only or should be accessible to - * applications using the {@link android.hardware.lights.LightsManager} API. - */ - private static boolean isSystemLight(HwLight light) { - // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system - // lights. Newly added lights will be made available via the - // LightsManager API. - return 0 <= light.type && light.type < LightsManager.LIGHT_ID_COUNT; - } - static native void setLight_native(int light, int color, int mode, int onMS, int offMS, int brightnessMode); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7f805bef520d..31dc09416ac8 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1579,7 +1579,9 @@ public class NotificationManagerService extends SystemService { } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { // turn off LED when user passes through lock screen - mNotificationLight.turnOff(); + if (mNotificationLight != null) { + mNotificationLight.turnOff(); + } } else if (action.equals(Intent.ACTION_USER_SWITCHED)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL); mUserProfiles.updateCache(context); @@ -4193,7 +4195,7 @@ public class NotificationManagerService extends SystemService { @Override public int getInterruptionFilterFromListener(INotificationListener token) throws RemoteException { - synchronized (mNotificationLight) { + synchronized (mNotificationLock) { return mInterruptionFilter; } } @@ -6773,7 +6775,7 @@ public class NotificationManagerService extends SystemService { if (canShowLightsLocked(record, aboveThreshold)) { mLights.add(key); updateLightsLocked(); - if (mUseAttentionLight) { + if (mUseAttentionLight && mAttentionLight != null) { mAttentionLight.pulse(); } blink = true; @@ -7974,6 +7976,10 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void updateLightsLocked() { + if (mNotificationLight == null) { + return; + } + // handle notification lights NotificationRecord ledNotification = null; while (ledNotification == null && !mLights.isEmpty()) { diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index c37ea8ba68bd..c97f33b5103f 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -16,22 +16,22 @@ package com.android.server.pm; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; import android.apex.IApexService; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; @@ -44,8 +44,9 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; +import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.utils.TimingsTraceAndSlog; @@ -61,7 +62,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; /** @@ -72,7 +75,7 @@ public abstract class ApexManager { private static final String TAG = "ApexManager"; - static final int MATCH_ACTIVE_PACKAGE = 1 << 0; + public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; static final int MATCH_FACTORY_PACKAGE = 1 << 1; private static final Singleton<ApexManager> sApexManagerSingleton = @@ -139,7 +142,14 @@ public abstract class ApexManager { */ public abstract List<ActiveApexInfo> getActiveApexInfos(); - abstract void systemReady(Context context); + /** + * Called by package manager service to scan apex package files when device boots up. + * + * @param packageParser The package parser which supports caches. + * @param executorService An executor to support parallel package parsing. + */ + abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService); /** * Retrieves information about an APEX package. @@ -154,7 +164,7 @@ public abstract class ApexManager { * is not found. */ @Nullable - abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); + public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); /** * Retrieves information about all active APEX packages. @@ -189,6 +199,27 @@ public abstract class ApexManager { abstract boolean isApexPackage(String packageName); /** + * Whether the APEX package is pre-installed or not. + * + * @param packageInfo the package to check + * @return {@code true} if this package is pre-installed, {@code false} otherwise. + */ + public static boolean isFactory(@NonNull PackageInfo packageInfo) { + return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + /** + * Returns the active apex package's name that contains the (apk) package. + * + * @param containedPackage The (apk) package that might be in a apex + * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside + * any apex. + */ + @Nullable + public abstract String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage); + + /** * Retrieves information about an apexd staged session i.e. the internal state used by apexd to * track the different states of a session. * @@ -342,7 +373,7 @@ public abstract class ApexManager { * difference between {@code packageName} and {@code apexModuleName}. */ @GuardedBy("mLock") - private Map<String, List<String>> mApksInApex = new ArrayMap<>(); + private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>(); @GuardedBy("mLock") private List<PackageInfo> mAllPackagesCache; @@ -357,7 +388,7 @@ public abstract class ApexManager { * the apk container to {@code apexModuleName} of the apex-payload inside. */ @GuardedBy("mLock") - private Map<String, String> mPackageNameToApexModuleName; + private ArrayMap<String, String> mPackageNameToApexModuleName; ApexManagerImpl(IApexService apexService) { mApexService = apexService; @@ -373,16 +404,6 @@ public abstract class ApexManager { return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; } - /** - * Whether the APEX package is pre-installed or not. - * - * @param packageInfo the package to check - * @return {@code true} if this package is pre-installed, {@code false} otherwise. - */ - private static boolean isFactory(PackageInfo packageInfo) { - return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } - @Override public List<ActiveApexInfo> getActiveApexInfos() { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", @@ -411,104 +432,94 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - // Post populateAllPackagesCacheIfNeeded to a background thread, since it's - // expensive to run it in broadcast handler thread. - BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded()); - context.unregisterReceiver(this); - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); - } - - private void populatePackageNameToApexModuleNameIfNeeded() { - synchronized (mLock) { - if (mPackageNameToApexModuleName != null) { - return; - } - try { - mPackageNameToApexModuleName = new ArrayMap<>(); - final ApexInfo[] allPkgs = mApexService.getAllPackages(); - for (int i = 0; i < allPkgs.length; i++) { - ApexInfo ai = allPkgs[i]; - PackageParser.PackageLite pkgLite; - try { - File apexFile = new File(ai.modulePath); - pkgLite = PackageParser.parsePackageLite(apexFile, 0); - } catch (PackageParser.PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " - + ai.modulePath, pe); - } - mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re); - throw new RuntimeException(re); + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackagesTraced"); + try { + synchronized (mLock) { + scanApexPackagesInternalLocked(packageParser, executorService); } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } - private void populateAllPackagesCacheIfNeeded() { - synchronized (mLock) { - if (mAllPackagesCache != null) { - return; - } - try { - mAllPackagesCache = new ArrayList<>(); - HashSet<String> activePackagesSet = new HashSet<>(); - HashSet<String> factoryPackagesSet = new HashSet<>(); - final ApexInfo[] allPkgs = mApexService.getAllPackages(); - for (ApexInfo ai : allPkgs) { - // If the device is using flattened APEX, don't report any APEX - // packages since they won't be managed or updated by PackageManager. - if ((new File(ai.modulePath)).isDirectory()) { - break; - } - int flags = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_SIGNATURES; - PackageParser.Package pkg; - try { - File apexFile = new File(ai.modulePath); - PackageParser pp = new PackageParser(); - pkg = pp.parsePackage(apexFile, flags, false); - PackageParser.collectCertificates(pkg, false); - } catch (PackageParser.PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); - } + @GuardedBy("mLock") + private void scanApexPackagesInternalLocked(PackageParser2 packageParser, + ExecutorService executorService) { + final ApexInfo[] allPkgs; + try { + mAllPackagesCache = new ArrayList<>(); + mPackageNameToApexModuleName = new ArrayMap<>(); + allPkgs = mApexService.getAllPackages(); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + throw new RuntimeException(re); + } + if (allPkgs.length == 0) { + return; + } + int flags = PackageManager.GET_META_DATA + | PackageManager.GET_SIGNING_CERTIFICATES + | PackageManager.GET_SIGNATURES; + ArrayMap<File, ApexInfo> parsingApexInfo = new ArrayMap<>(); + ParallelPackageParser parallelPackageParser = + new ParallelPackageParser(packageParser, executorService); + + for (ApexInfo ai : allPkgs) { + File apexFile = new File(ai.modulePath); + parallelPackageParser.submit(apexFile, flags); + parsingApexInfo.put(apexFile, ai); + } - final PackageInfo packageInfo = - PackageParser.generatePackageInfo(pkg, ai, flags); - mAllPackagesCache.add(packageInfo); - if (ai.isActive) { - if (activePackagesSet.contains(packageInfo.packageName)) { - throw new IllegalStateException( - "Two active packages have the same name: " - + packageInfo.packageName); - } - activePackagesSet.add(packageInfo.packageName); + HashSet<String> activePackagesSet = new HashSet<>(); + HashSet<String> factoryPackagesSet = new HashSet<>(); + // Process results one by one + for (int i = 0; i < parsingApexInfo.size(); i++) { + ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); + Throwable throwable = parseResult.throwable; + ApexInfo ai = parsingApexInfo.get(parseResult.scanFile); + + if (throwable == null) { + final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate( + parseResult.parsedPackage, ai, flags); + if (packageInfo == null) { + throw new IllegalStateException("Unable to generate package info: " + + ai.modulePath); + } + mAllPackagesCache.add(packageInfo); + mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); + if (ai.isActive) { + if (activePackagesSet.contains(packageInfo.packageName)) { + throw new IllegalStateException( + "Two active packages have the same name: " + + packageInfo.packageName); } - if (ai.isFactory) { - if (factoryPackagesSet.contains(packageInfo.packageName)) { - throw new IllegalStateException( - "Two factory packages have the same name: " - + packageInfo.packageName); - } - factoryPackagesSet.add(packageInfo.packageName); + activePackagesSet.add(packageInfo.packageName); + } + if (ai.isFactory) { + if (factoryPackagesSet.contains(packageInfo.packageName)) { + throw new IllegalStateException( + "Two factory packages have the same name: " + + packageInfo.packageName); } + factoryPackagesSet.add(packageInfo.packageName); } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); - throw new RuntimeException(re); + } else if (throwable instanceof PackageParser.PackageParserException) { + throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); + } else { + throw new IllegalStateException("Unexpected exception occurred while parsing " + + ai.modulePath, throwable); } } } @Override - @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) { - populateAllPackagesCacheIfNeeded(); + @Nullable + public PackageInfo getPackageInfo(String packageName, + @PackageInfoFlags int flags) { + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; for (PackageInfo packageInfo: mAllPackagesCache) { @@ -525,7 +536,8 @@ public abstract class ApexManager { @Override List<PackageInfo> getActivePackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> isActive(item)) @@ -534,7 +546,8 @@ public abstract class ApexManager { @Override List<PackageInfo> getFactoryPackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> isFactory(item)) @@ -543,7 +556,8 @@ public abstract class ApexManager { @Override List<PackageInfo> getInactivePackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> !isActive(item)) @@ -553,7 +567,8 @@ public abstract class ApexManager { @Override boolean isApexPackage(String packageName) { if (!isApexSupported()) return false; - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); for (PackageInfo packageInfo : mAllPackagesCache) { if (packageInfo.packageName.equals(packageName)) { return true; @@ -563,6 +578,36 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); + + Objects.requireNonNull(containedPackage); + + synchronized (mLock) { + int numApksInApex = mApksInApex.size(); + for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) { + if (mApksInApex.valueAt(apkInApexNum).contains( + containedPackage.getPackageName())) { + String apexModuleName = mApksInApex.keyAt(apkInApexNum); + + int numApexPkgs = mPackageNameToApexModuleName.size(); + for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) { + if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals( + apexModuleName)) { + return mPackageNameToApexModuleName.keyAt(apexPkgNum); + } + } + } + } + } + + return null; + } + + @Override @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { try { ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId); @@ -684,8 +729,9 @@ public abstract class ApexManager { @Override List<String> getApksInApex(String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); String moduleName = mPackageNameToApexModuleName.get(apexPackageName); if (moduleName == null) { return Collections.emptyList(); @@ -697,17 +743,19 @@ public abstract class ApexManager { @Override @Nullable public String getApexModuleNameForPackageName(String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); return mPackageNameToApexModuleName.get(apexPackageName); } } @Override public long snapshotCeData(int userId, int rollbackId, String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); String apexModuleName; synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); } if (apexModuleName == null) { @@ -724,9 +772,10 @@ public abstract class ApexManager { @Override public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); String apexModuleName; synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); } if (apexModuleName == null) { @@ -797,15 +846,7 @@ public abstract class ApexManager { void dump(PrintWriter pw, @Nullable String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); try { - populateAllPackagesCacheIfNeeded(); ipw.println(); - ipw.println("Active APEX packages:"); - dumpFromPackagesCache(getActivePackages(), packageName, ipw); - ipw.println("Inactive APEX packages:"); - dumpFromPackagesCache(getInactivePackages(), packageName, ipw); - ipw.println("Factory APEX packages:"); - dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); - ipw.increaseIndent(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); @@ -834,6 +875,17 @@ public abstract class ApexManager { ipw.decreaseIndent(); } ipw.decreaseIndent(); + ipw.println(); + if (mAllPackagesCache == null) { + ipw.println("APEX packages have not been scanned"); + return; + } + ipw.println("Active APEX packages:"); + dumpFromPackagesCache(getActivePackages(), packageName, ipw); + ipw.println("Inactive APEX packages:"); + dumpFromPackagesCache(getInactivePackages(), packageName, ipw); + ipw.println("Factory APEX packages:"); + dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } @@ -879,12 +931,13 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { // No-op } @Override - PackageInfo getPackageInfo(String packageName, int flags) { + public PackageInfo getPackageInfo(String packageName, int flags) { return null; } @@ -909,6 +962,15 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage) { + Objects.requireNonNull(containedPackage); + + return null; + } + + @Override ApexSessionInfo getStagedSessionInfo(int sessionId) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java index 7069818e3894..40876754eae8 100644 --- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java @@ -64,6 +64,7 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private static final String TAG = "CrossProfileAppsService"; @@ -447,7 +448,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { final int uid = mInjector.getPackageManager() .getPackageUidAsUser(packageName, /* flags= */ 0, userId); if (currentModeEquals(newMode, packageName, uid)) { - Slog.w(TAG, "Attempt to set mode to existing value of " + newMode + " for " + Slog.i(TAG, "Attempt to set mode to existing value of " + newMode + " for " + packageName + " on user ID " + userId); return; } @@ -577,6 +578,24 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op)); } + @Override + public void clearInteractAcrossProfilesAppOps() { + final int defaultMode = + AppOpsManager.opToDefaultMode( + AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES)); + findAllPackageNames() + .forEach(packageName -> setInteractAcrossProfilesAppOp(packageName, defaultMode)); + } + + private List<String> findAllPackageNames() { + return mInjector.getPackageManagerInternal() + .getInstalledApplications( + /* flags= */ 0, mInjector.getCallingUserId(), mInjector.getCallingUid()) + .stream() + .map(applicationInfo -> applicationInfo.packageName) + .collect(Collectors.toList()); + } + CrossProfileAppsInternal getLocalService() { return mLocalService; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 2ff3d2a3a938..8ff7ea9d61dd 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -490,6 +490,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements throw new SecurityException("User restriction prevents installing"); } + if (params.dataLoaderParams != null + && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the " + + "com.android.permission.USE_INSTALLER_V2 permission " + + "to use a data loader"); + } + String requestedInstallerPackageName = params.installerPackageName != null ? params.installerPackageName : installerPackageName; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 348a4cb77163..42ed20654a2c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -2479,12 +2479,14 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public DataLoaderParamsParcel getDataLoaderParams() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null; } @Override public void addFile(int location, String name, long lengthBytes, byte[] metadata, byte[] signature) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); @@ -2517,6 +2519,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void removeFile(int location, String name) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null); if (!isDataLoaderInstallation()) { throw new IllegalStateException( "Cannot add files to non-data loader installation session."); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 58a9d9c5f6bf..34363c8df8f6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2900,6 +2900,9 @@ public class PackageManagerService extends IPackageManager.Stub mMetrics, mCacheDir, mPackageParserCallback); ExecutorService executorService = ParallelPackageParser.makeExecutorService(); + // Prepare apex package info before scanning APKs, these information are needed when + // scanning apk in apex. + mApexManager.scanApexPackagesTraced(packageParser, executorService); // Collect vendor/product/system_ext overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they @@ -20696,7 +20699,6 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); - mApexManager.systemReady(mContext); mPackageDexOptimizer.systemReady(); mInjector.getStorageManagerInternal().addExternalStoragePolicy( diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 04c965e69588..765ecb9710cb 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -42,6 +42,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED; +import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS; @@ -130,6 +131,7 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; +import com.android.server.pm.ApexManager; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; @@ -3317,8 +3319,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged() && !platformPackage && platformPermission) { if (!hasPrivappWhitelistEntry(perm, pkg)) { - // Only report violations for apps on system image - if (!mSystemReady && !pkgSetting.getPkgState().isUpdatedSystemApp()) { + ApexManager apexMgr = ApexManager.getInstance(); + String apexContainingPkg = apexMgr.getActiveApexPackageNameContainingPackage(pkg); + + // Only enforce whitelist this on boot + if (!mSystemReady + // Updated system apps do not need to be whitelisted + && !pkgSetting.getPkgState().isUpdatedSystemApp() + // Apps that are in updated apexs' do not need to be whitelisted + && (apexContainingPkg == null || apexMgr.isFactory( + apexMgr.getPackageInfo(apexContainingPkg, MATCH_ACTIVE_PACKAGE)))) { // it's only a reportable violation if the permission isn't explicitly denied ArraySet<String> deniedPermissions = null; if (pkg.isVendor()) { diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java index 39aeafc345ea..d6c48a00d33d 100644 --- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java @@ -26,6 +26,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INST import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY; import static java.lang.Integer.min; @@ -36,6 +37,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManagerInternal; @@ -63,6 +65,9 @@ public abstract class SoftRestrictedPermissionPolicy { } }; + private static final boolean isLegacyStorageAppOpStickyGlobal = SystemProperties.getBoolean( + PROP_LEGACY_OP_STICKY, /*defaultValue*/true); + /** * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over * what to set, always compute the combined targetSDK. @@ -136,9 +141,12 @@ public abstract class SoftRestrictedPermissionPolicy { shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage() && smInternal.hasLegacyExternalStorage(appInfo.uid); targetSDK = getMinimumTargetSDK(context, appInfo, user); + // LEGACY_STORAGE op is normally sticky for apps targetig <= Q. + // However, this device can be configured to make it non-sticky. + boolean isLegacyAppOpSticky = isLegacyStorageAppOpStickyGlobal + && targetSDK <= Build.VERSION_CODES.Q; shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0 - || (targetSDK > Build.VERSION_CODES.Q - && !shouldPreserveLegacyExternalStorage); + || (!isLegacyAppOpSticky && !shouldPreserveLegacyExternalStorage); } else { isWhiteListed = false; shouldApplyRestriction = false; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 5025835b5a59..86ff926af7d4 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -3531,7 +3531,9 @@ public final class PowerManagerService extends SystemService } // Control light outside of lock. - light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + if (light != null) { + light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + } } private void setDozeAfterScreenOffInternal(boolean on) { diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 74c3a9ec375b..2783d0b83254 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -767,7 +767,7 @@ public class ThermalManagerService extends SystemService { protected boolean connectToHal() { synchronized (mHalLock) { try { - mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(); + mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(true); mThermalHal10.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); Slog.i(TAG, @@ -902,7 +902,7 @@ public class ThermalManagerService extends SystemService { protected boolean connectToHal() { synchronized (mHalLock) { try { - mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(); + mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(true); mThermalHal11.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); mThermalHal11.registerThermalCallback(mThermalCallback11); @@ -1046,7 +1046,7 @@ public class ThermalManagerService extends SystemService { protected boolean connectToHal() { synchronized (mHalLock) { try { - mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(); + mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(true); mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE); mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false, 0 /* not used */); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java index b051bab71f12..8ff2a1b6364b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java @@ -37,9 +37,9 @@ class TimeZoneDetectorShellCommand extends ShellCommand { } switch (cmd) { - case "suggestTelephonyTimeZone": + case "suggest_telephony_time_zone": return runSuggestTelephonyTimeZone(); - case "suggestManualTimeZone": + case "suggest_manual_time_zone": return runSuggestManualTimeZone(); default: { return handleDefaultCommands(cmd); @@ -105,9 +105,9 @@ class TimeZoneDetectorShellCommand extends ShellCommand { pw.println("Time Zone Detector (time_zone_detector) commands:"); pw.println(" help"); pw.println(" Print this help text."); - pw.println(" suggestTelephonyTimeZone"); + pw.println(" suggest_telephony_time_zone"); pw.println(" --suggestion <telephony suggestion opts>"); - pw.println(" suggestManualTimeZone"); + pw.println(" suggest_manual_time_zone"); pw.println(" --suggestion <manual suggestion opts>"); pw.println(); ManualTimeZoneSuggestion.printCommandLineOpts(pw); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 722d9f7e7c38..ddf166eb0bd5 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2441,9 +2441,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub * the caller here writes new bitmap data. */ if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) { - if (DEBUG) { - Slog.i(TAG, "Migrating system->lock to preserve"); - } + Slog.i(TAG, "Migrating current wallpaper to be lock-only before" + + "updating system wallpaper"); migrateSystemToLockWallpaperLocked(userId); } @@ -2511,6 +2510,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile, MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE); if (!SELinux.restorecon(wallpaper.wallpaperFile)) { + Slog.w(TAG, "restorecon failed for wallpaper file: " + + wallpaper.wallpaperFile.getPath()); return null; } wallpaper.name = name; @@ -2520,10 +2521,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // Nullify field to require new computation wallpaper.primaryColors = null; - if (DEBUG) { - Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId - + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); - } + Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId + + " name=" + name + " file=" + wallpaper.wallpaperFile.getName()); return fd; } catch (FileNotFoundException e) { Slog.w(TAG, "Error setting wallpaper", e); @@ -2556,7 +2555,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData wallpaper; synchronized (mLock) { - if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name); + Slog.v(TAG, "setWallpaperComponent name=" + name); wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); @@ -2571,6 +2570,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (mLockWallpaperMap.get(userId) == null) { // We're using the static imagery and there is no lock-specific image in place, // therefore it's a shared system+lock image that we need to migrate. + Slog.i(TAG, "Migrating current wallpaper to be lock-only before" + + "updating system wallpaper"); migrateSystemToLockWallpaperLocked(userId); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 3477d82f4d71..2f814f598a05 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2085,7 +2085,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** Returns true if this activity is opaque and fills the entire space of this task. */ boolean occludesParent() { - return mOccludesParent; + return !finishing && mOccludesParent; } boolean setOccludesParent(boolean occludesParent) { diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index fa0ad505f1a9..90898597c265 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2342,8 +2342,7 @@ class ActivityStack extends Task { if (!newTask && isOrhasTask) { // Starting activity cannot be occluding activity, otherwise starting window could be // remove immediately without transferring to starting activity. - final ActivityRecord occludingActivity = getActivity( - (ar) -> !ar.finishing && ar.occludesParent(), true, r); + final ActivityRecord occludingActivity = getOccludingActivityAbove(r); if (occludingActivity != null) { // Here it is! Now, if this is not yet visible (occluded by another task) to the // user, then just add it without starting; it will get started when the user @@ -3069,6 +3068,14 @@ class ActivityStack extends Task { task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds); } + /** + * Returns the top-most activity that occludes the given one, or @{code null} if none. + */ + @Nullable + private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) { + return getActivity((ar) -> ar.occludesParent(), true /* traverseTopToBottom */, activity); + } + boolean willActivityBeVisible(IBinder token) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r == null) { @@ -3076,9 +3083,7 @@ class ActivityStack extends Task { } // See if there is an occluding activity on-top of this one. - final ActivityRecord occludingActivity = getActivity((ar) -> - ar.occludesParent() && !ar.finishing, - r, false /*includeBoundary*/, true /*traverseTopToBottom*/); + final ActivityRecord occludingActivity = getOccludingActivityAbove(r); if (occludingActivity != null) return false; if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false," diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a5b0026398b6..bf92542cdd63 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -32,7 +32,6 @@ import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; @@ -2615,13 +2614,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { - return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED); + return getFilteredTasks(maxNum, false /* filterForVisibleRecents */); } + /** + * @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever + * be visible in the recent task list in systemui + */ @Override public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, - @WindowConfiguration.ActivityType int ignoreActivityType, - @WindowConfiguration.WindowingMode int ignoreWindowingMode) { + boolean filterOnlyVisibleRecents) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final boolean crossUser = isCrossUserAllowed(callingPid, callingUid); @@ -2637,8 +2639,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum); final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid); - mRootWindowContainer.getRunningTasks(maxNum, list, ignoreActivityType, - ignoreWindowingMode, callingUid, allowed, crossUser, callingProfileIds); + mRootWindowContainer.getRunningTasks(maxNum, list, filterOnlyVisibleRecents, callingUid, + allowed, crossUser, callingProfileIds); } return list; diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 6a47c9e217f8..654ccc80f8a8 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -618,6 +618,7 @@ public class AppTransitionController { Animation anim = mService.mPolicy.createKeyguardWallpaperExit( (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); if (anim != null) { + anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); mDisplayContent.mWallpaperController.startWallpaperAnimation(anim); } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6c5428c69377..88c9b2cc0cb9 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1317,8 +1317,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mDisplayRotation.isWaitingForRemoteRotation()) { return; } - // Clear the record because the display will sync to current rotation. - mFixedRotationLaunchingApp = null; final boolean configUpdated = updateDisplayOverrideConfigurationLocked(); if (configUpdated) { @@ -1509,7 +1507,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo startFixedRotationTransform(r, rotation); mAppTransition.registerListenerLocked(new WindowManagerInternal.AppTransitionListener() { void done() { - r.clearFixedRotationTransform(); + r.finishFixedRotationTransform(); mAppTransition.unregisterListener(this); } @@ -1538,7 +1536,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (token != mFixedRotationLaunchingApp) { return false; } - if (updateOrientation()) { + // Update directly because the app which will change the orientation of display is ready. + if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) { sendNewConfiguration(); return true; } @@ -1584,7 +1583,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @param oldRotation the rotation we are coming from. * @param rotation the rotation to apply. */ - void applyRotationLocked(final int oldRotation, final int rotation) { + private void applyRotation(final int oldRotation, final int rotation) { mDisplayRotation.applyCurrentRotation(rotation); final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly(); final Transaction transaction = getPendingTransaction(); @@ -3210,7 +3209,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mode == UPDATE_FOCUS_PLACING_SURFACES) { performLayout(true /*initial*/, updateInputWindows); } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) { - mWmService.mRoot.performSurfacePlacement(false); + mWmService.mRoot.performSurfacePlacement(); } } @@ -3847,7 +3846,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } // TODO: Super crazy long method that should be broken down... - void applySurfaceChangesTransaction(boolean recoveringMemory) { + void applySurfaceChangesTransaction() { final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked; mTmpUpdateAllDrawn.clear(); @@ -4432,6 +4431,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo position = findPositionForStack(position, stack, true /* adding */); super.addChild(stack, position); + mAtmService.updateSleepIfNeededLocked(); // The reparenting case is handled in WindowContainer. if (!stack.mReparenting) { @@ -4443,6 +4443,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo protected void removeChild(ActivityStack stack) { super.removeChild(stack); mDisplayContent.onStackRemoved(stack); + mAtmService.updateSleepIfNeededLocked(); removeStackReferenceIfNeeded(stack); } @@ -5648,7 +5649,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo void addStack(ActivityStack stack, int position) { setStackOnDisplay(stack, position); positionStackAt(stack, position); - mAtmService.updateSleepIfNeededLocked(); } void addStackReferenceIfNeeded(ActivityStack stack) { @@ -5667,7 +5667,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mPreferredTopFocusableStack = null; } releaseSelfIfNeeded(); - mAtmService.updateSleepIfNeededLocked(); onStackOrderChanged(stack); } @@ -6493,15 +6492,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { - final int currRotation = - getRequestedOverrideConfiguration().windowConfiguration.getRotation(); - if (currRotation != ROTATION_UNDEFINED - && currRotation != overrideConfiguration.windowConfiguration.getRotation()) { - applyRotationLocked(currRotation, - overrideConfiguration.windowConfiguration.getRotation()); - } - mCurrentOverrideConfigurationChanges = - getRequestedOverrideConfiguration().diff(overrideConfiguration); + final Configuration currOverrideConfig = getRequestedOverrideConfiguration(); + final int currRotation = currOverrideConfig.windowConfiguration.getRotation(); + final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation(); + if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) { + if (mFixedRotationLaunchingApp != null) { + mFixedRotationLaunchingApp.clearFixedRotationTransform( + () -> applyRotation(currRotation, overrideRotation)); + // Clear the record because the display will sync to current rotation. + mFixedRotationLaunchingApp = null; + } else { + applyRotation(currRotation, overrideRotation); + } + } + mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration); super.onRequestedOverrideConfigurationChanged(overrideConfiguration); mCurrentOverrideConfigurationChanges = 0; mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 60b817c13236..af89a05bfa62 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -484,11 +484,8 @@ public class DisplayRotation { prepareNormalRotationAnimation(); } - // TODO(b/147469351): Remove the restriction. - if (mDisplayContent.mFixedRotationLaunchingApp == null) { - // Give a remote handler (system ui) some time to reposition things. - startRemoteRotation(oldRotation, mRotation); - } + // Give a remote handler (system ui) some time to reposition things. + startRemoteRotation(oldRotation, mRotation); return true; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 88cdd1781aee..18332b9484c0 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -62,7 +62,7 @@ final class InputMonitor { // When true, need to call updateInputWindowsLw(). private boolean mUpdateInputWindowsNeeded = true; private boolean mUpdateInputWindowsPending; - private boolean mApplyImmediately; + private boolean mUpdateInputWindowsImmediately; // Currently focused input window handle. private InputWindowHandle mFocusedInputWindowHandle; @@ -347,14 +347,20 @@ final class InputMonitor { } } - void updateInputWindowsImmediately() { + /** + * Immediately update the input transaction and merge into the passing Transaction that could be + * collected and applied later. + */ + void updateInputWindowsImmediately(SurfaceControl.Transaction t) { mHandler.removeCallbacks(mUpdateInputWindows); - mApplyImmediately = true; + mUpdateInputWindowsImmediately = true; mUpdateInputWindows.run(); - mApplyImmediately = false; + mUpdateInputWindowsImmediately = false; + t.merge(mInputTransaction); } - /* Called when the current input focus changes. + /** + * Called when the current input focus changes. * Layer assignment is assumed to be complete by the time this is called. */ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { @@ -465,10 +471,7 @@ final class InputMonitor { if (mAddWallpaperInputConsumerHandle) { mWallpaperInputConsumer.show(mInputTransaction, 0); } - - if (mApplyImmediately) { - mInputTransaction.apply(); - } else { + if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index ac6e75c717ff..fda70d14db2b 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -97,7 +97,8 @@ class InsetsPolicy { private void updateHideNavInputEventReceiver() { mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR), - mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH); + mFocusedWin != null + && mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH); } boolean isHidden(@InternalInsetsType int type) { diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 244ba82ce32c..12be9df55fad 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -1309,6 +1310,7 @@ class RecentTasks { == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) { return false; } + break; } // Ignore certain windowing modes @@ -1316,23 +1318,21 @@ class RecentTasks { case WINDOWING_MODE_PINNED: return false; case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: - if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask()); + if (DEBUG_RECENTS_TRIM_TASKS) { + Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask()); + } final ActivityStack stack = task.getStack(); if (stack != null && stack.getTopMostTask() == task) { // Only the non-top task of the primary split screen mode is visible return false; } - } - - // Tasks managed by/associated with an ActivityView should be excluded from recents. - // singleTaskInstance is set on the VirtualDisplay managed by ActivityView - // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance - final ActivityStack stack = task.getStack(); - if (stack != null) { - DisplayContent display = stack.getDisplay(); - if (display != null && display.isSingleTaskInstance()) { - return false; - } + break; + case WINDOWING_MODE_MULTI_WINDOW: + // Ignore tasks that are always on top + if (task.isAlwaysOnTop()) { + return false; + } + break; } // If we're in lock task mode, ignore the root task diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 9089240fe9d1..057592c0c2fc 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -400,6 +400,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, throw e; } finally { mService.continueWindowLayout(); + // Make sure the surfaces are updated with the latest state. Sometimes the + // surface placement may be skipped if display configuration is changed (i.e. + // {@link DisplayContent#mWaitingForConfig} is true). + if (mWindowManager.mRoot.isLayoutNeeded()) { + mWindowManager.mRoot.performSurfacePlacement(); + } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 3e5cb50d6101..a30b70de267d 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -667,7 +667,7 @@ public class RecentsAnimationController implements DeathRecipient { mTargetActivityRecord.token); } if (mTargetActivityRecord.hasFixedRotationTransform()) { - mTargetActivityRecord.clearFixedRotationTransform(); + mTargetActivityRecord.finishFixedRotationTransform(); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index b2920ee1d4aa..ff174278878f 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -796,10 +796,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return leakedSurface || killedApps; } - void performSurfacePlacement(boolean recoveringMemory) { + void performSurfacePlacement() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement"); try { - performSurfacePlacementNoTrace(recoveringMemory); + performSurfacePlacementNoTrace(); } finally { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -807,7 +807,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // "Something has changed! Let's make it correct now." // TODO: Super crazy long method that should be broken down... - void performSurfacePlacementNoTrace(boolean recoveringMemory) { + void performSurfacePlacementNoTrace() { if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by " + Debug.getCallers(3)); @@ -842,7 +842,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges"); mWmService.openSurfaceTransaction(); try { - applySurfaceChangesTransaction(recoveringMemory); + applySurfaceChangesTransaction(); } catch (RuntimeException e) { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { @@ -1042,7 +1042,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - private void applySurfaceChangesTransaction(boolean recoveringMemory) { + private void applySurfaceChangesTransaction() { mHoldScreenWindow = null; mObscuringWindow = null; @@ -1065,7 +1065,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int count = mChildren.size(); for (int j = 0; j < count; ++j) { final DisplayContent dc = mChildren.get(j); - dc.applySurfaceChangesTransaction(recoveringMemory); + dc.applySurfaceChangesTransaction(); } // Give the display manager a chance to adjust properties like display rotation if it needs @@ -2852,7 +2852,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param candidateTask The possible task the activity might be put in. * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. */ - private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, + @VisibleForTesting + ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, @Nullable Task candidateTask, @Nullable ActivityOptions options, @Nullable LaunchParamsController.LaunchParams launchParams) { final DisplayContent displayContent = getDisplayContentOrCreate(displayId); @@ -2873,6 +2874,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> if (attachedDisplayId == INVALID_DISPLAY || attachedDisplayId == displayId) { return candidateTask.getStack(); } + // Or the candidate task is already a root task that can be reused by reparenting + // it to the target display. + if (candidateTask.isRootTask()) { + final ActivityStack stack = candidateTask.getStack(); + displayContent.moveStackToDisplay(stack, true /* onTop */); + return stack; + } } int windowingMode; @@ -3385,11 +3393,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> @VisibleForTesting void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list, - @WindowConfiguration.ActivityType int ignoreActivityType, - @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid, - boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) { - mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType, - ignoreWindowingMode, this, callingUid, allowed, crossUser, profileIds); + boolean filterOnlyVisibleRecents, int callingUid, boolean allowed, boolean crossUser, + ArraySet<Integer> profileIds) { + mStackSupervisor.getRunningTasks().getTasks(maxNum, list, filterOnlyVisibleRecents, this, + callingUid, allowed, crossUser, profileIds); } void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) { diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 02077fbf453e..3509ba72d058 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -16,12 +16,10 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import android.app.ActivityManager.RunningTaskInfo; -import android.app.WindowConfiguration.ActivityType; -import android.app.WindowConfiguration.WindowingMode; import android.os.UserHandle; import android.util.ArraySet; @@ -49,13 +47,13 @@ class RunningTasks { private boolean mCrossUser; private ArraySet<Integer> mProfileIds; private boolean mAllowed; - private int mIgnoreActivityType; - private int mIgnoreWindowingMode; + private boolean mFilterOnlyVisibleRecents; private ActivityStack mTopDisplayFocusStack; + private RecentTasks mRecentTasks; - void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType, - @WindowingMode int ignoreWindowingMode, RootWindowContainer root, - int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) { + void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents, + RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser, + ArraySet<Integer> profileIds) { // Return early if there are no tasks to fetch if (maxNum <= 0) { return; @@ -68,9 +66,9 @@ class RunningTasks { mCrossUser = crossUser; mProfileIds = profileIds; mAllowed = allowed; - mIgnoreActivityType = ignoreActivityType; - mIgnoreWindowingMode = ignoreWindowingMode; + mFilterOnlyVisibleRecents = filterOnlyVisibleRecents; mTopDisplayFocusStack = root.getTopDisplayFocusedStack(); + mRecentTasks = root.mService.getRecentTasks(); final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, PooledLambda.__(Task.class)); @@ -107,14 +105,12 @@ class RunningTasks { return; } } - if (mIgnoreActivityType != ACTIVITY_TYPE_UNDEFINED - && task.getActivityType() == mIgnoreActivityType) { - // Skip ignored activity type - return; - } - if (mIgnoreWindowingMode != WINDOWING_MODE_UNDEFINED - && task.getWindowingMode() == mIgnoreWindowingMode) { - // Skip ignored windowing mode + if (mFilterOnlyVisibleRecents + && task.getActivityType() != ACTIVITY_TYPE_HOME + && task.getActivityType() != ACTIVITY_TYPE_RECENTS + && !mRecentTasks.isVisibleRecentTask(task)) { + // Skip if this task wouldn't be visibile (ever) from recents, with an exception for the + // home & recent tasks return; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f19c10637c9b..7a41ea57610d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4111,8 +4111,7 @@ class Task extends WindowContainer<WindowContainer> { return true; } - // Called on Binder death. - void taskOrganizerDied() { + void taskOrganizerUnregistered() { mTaskOrganizer = null; mLastTaskOrganizerWindowingMode = -1; onTaskOrganizerChanged(); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 05b721b8ee81..4382e9d578ad 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -34,6 +34,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import android.util.SparseArray; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.IWindowContainer; @@ -42,6 +43,7 @@ import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; @@ -51,6 +53,7 @@ import java.util.WeakHashMap; */ class TaskOrganizerController extends ITaskOrganizerController.Stub { private static final String TAG = "TaskOrganizerController"; + private static final LinkedList<TaskOrganizerState> EMPTY_LIST = new LinkedList<>(); /** * Masks specifying which configurations are important to report back to an organizer when @@ -73,32 +76,20 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void binderDied() { synchronized (mGlobalLock) { - final TaskOrganizerState state = - mTaskOrganizerStates.get(mTaskOrganizer.asBinder()); - state.releaseTasks(); - mTaskOrganizerStates.remove(mTaskOrganizer.asBinder()); - if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) { - mTaskOrganizersForWindowingMode.remove(mWindowingMode); - } + final TaskOrganizerState state = mTaskOrganizerStates.remove( + mTaskOrganizer.asBinder()); + state.dispose(); } } }; - class TaskOrganizerState { - ITaskOrganizer mOrganizer; - DeathRecipient mDeathRecipient; - int mWindowingMode; - - ArrayList<Task> mOrganizedTasks = new ArrayList<>(); - - // Save the TaskOrganizer which we replaced registration for - // so it can be re-registered if we unregister. - TaskOrganizerState mReplacementFor; - boolean mDisposed = false; + private class TaskOrganizerState { + private final ITaskOrganizer mOrganizer; + private final DeathRecipient mDeathRecipient; + private final int mWindowingMode; + private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); - - TaskOrganizerState(ITaskOrganizer organizer, int windowingMode, - @Nullable TaskOrganizerState replacing) { + TaskOrganizerState(ITaskOrganizer organizer, int windowingMode) { mOrganizer = organizer; mDeathRecipient = new DeathRecipient(organizer, windowingMode); try { @@ -107,7 +98,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { Slog.e(TAG, "TaskOrganizer failed to register death recipient"); } mWindowingMode = windowingMode; - mReplacementFor = replacing; } void addTask(Task t) { @@ -129,35 +119,26 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void dispose() { - mDisposed = true; releaseTasks(); - handleReplacement(); + mTaskOrganizersForWindowingMode.get(mWindowingMode).remove(this); } - void releaseTasks() { + private void releaseTasks() { for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) { final Task t = mOrganizedTasks.get(i); - t.taskOrganizerDied(); removeTask(t); - } - } - - void handleReplacement() { - if (mReplacementFor != null && !mReplacementFor.mDisposed) { - mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor); + t.taskOrganizerUnregistered(); } } void unlinkDeath() { - mDisposed = true; mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0); } - }; - - - final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap(); - final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); + } + private final SparseArray<LinkedList<TaskOrganizerState>> mTaskOrganizersForWindowingMode = + new SparseArray<>(); + private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); @@ -196,11 +177,17 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { Slog.w(TAG, "Task organizer already exists for windowing mode: " + windowingMode); } - final TaskOrganizerState previousState = - mTaskOrganizersForWindowingMode.get(windowingMode); - final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode, - previousState); - mTaskOrganizersForWindowingMode.put(windowingMode, state); + + LinkedList<TaskOrganizerState> states; + if (mTaskOrganizersForWindowingMode.contains(windowingMode)) { + states = mTaskOrganizersForWindowingMode.get(windowingMode); + } else { + states = new LinkedList<>(); + mTaskOrganizersForWindowingMode.put(windowingMode, states); + } + final TaskOrganizerState previousState = states.peekLast(); + final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode); + states.add(state); mTaskOrganizerStates.put(organizer.asBinder(), state); if (previousState == null) { @@ -221,16 +208,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void unregisterTaskOrganizer(ITaskOrganizer organizer) { - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); + final TaskOrganizerState state = mTaskOrganizerStates.remove(organizer.asBinder()); state.unlinkDeath(); - if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) { - mTaskOrganizersForWindowingMode.remove(state.mWindowingMode); - } state.dispose(); } ITaskOrganizer getTaskOrganizer(int windowingMode) { - final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode); + final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode, + EMPTY_LIST).peekLast(); if (state == null) { return null; } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index f046e8adc478..be0d6f8a0b9f 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -268,8 +268,9 @@ class TaskPositioner implements IBinder.DeathRecipient { mDisplayContent.getDisplayRotation().pause(); // Notify InputMonitor to take mDragWindowHandle. - mDisplayContent.getInputMonitor().updateInputWindowsImmediately(); - new SurfaceControl.Transaction().syncInputWindows().apply(true); + final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); + mDisplayContent.getInputMonitor().updateInputWindowsImmediately(t); + t.syncInputWindows().apply(); final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics(); mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics); diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 57d0a335a688..29a2e18f46a8 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -142,11 +142,13 @@ class WallpaperController { mFindResults.setUseTopWallpaperAsTarget(true); } - final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null - && w.mActivityRecord.isAnimating(TRANSITION | PARENTS) - && AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit()) - && (w.mActivityRecord.getTransitFlags() - & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); + final WindowContainer animatingContainer = w.mActivityRecord != null + ? w.mActivityRecord.getAnimatingContainer() : null; + final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null + && animatingContainer.isAnimating(TRANSITION | PARENTS) + && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit) + && (animatingContainer.mTransitFlags + & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0); boolean needsShowWhenLockedWallpaper = false; if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 @@ -166,8 +168,6 @@ class WallpaperController { final RecentsAnimationController recentsAnimationController = mService.getRecentsAnimationController(); - final WindowContainer animatingContainer = - w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null; final boolean animationWallpaper = animatingContainer != null && animatingContainer.getAnimation() != null && animatingContainer.getAnimation().getShowWallpaper(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 23ba528b6aee..075772566d56 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7746,19 +7746,23 @@ public class WindowManagerService extends IWindowManager.Stub public void syncInputTransactions() { waitForAnimationsToComplete(); + // Collect all input transactions from all displays to make sure we could sync all input + // windows at same time. + final SurfaceControl.Transaction t = mTransactionFactory.get(); synchronized (mGlobalLock) { mWindowPlacerLocked.performSurfacePlacementIfScheduled(); mRoot.forAllDisplays(displayContent -> - displayContent.getInputMonitor().updateInputWindowsImmediately()); + displayContent.getInputMonitor().updateInputWindowsImmediately(t)); } - mTransactionFactory.get().syncInputWindows().apply(true); + t.syncInputWindows().apply(); } private void waitForAnimationsToComplete() { synchronized (mGlobalLock) { long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; - while (mRoot.isAnimating(TRANSITION | CHILDREN) && timeoutRemaining > 0) { + while ((mAnimator.isAnimationScheduled() + || mRoot.isAnimating(TRANSITION | CHILDREN)) && timeoutRemaining > 0) { long startTime = System.currentTimeMillis(); try { mGlobalLock.wait(timeoutRemaining); diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 0cfdebc6792d..6b9fbcbf459f 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -157,9 +157,7 @@ class WindowSurfacePlacer { mInLayout = true; - boolean recoveringMemory = false; if (!mService.mForceRemoves.isEmpty()) { - recoveringMemory = true; // Wait a little bit for things to settle down, and off we go. while (!mService.mForceRemoves.isEmpty()) { final WindowState ws = mService.mForceRemoves.remove(0); @@ -177,7 +175,7 @@ class WindowSurfacePlacer { } try { - mService.mRoot.performSurfacePlacement(recoveringMemory); + mService.mRoot.performSurfacePlacement(); mInLayout = false; diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 850c36238513..3c2b6ec9711d 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -480,26 +480,42 @@ class WindowToken extends WindowContainer<WindowState> { } /** - * Clears the transformation and continue updating the orientation change of display. Only the - * state owner can clear the transform state. + * Finishes the transform and continue updating the orientation change of display. Only the + * state owner can finish the transform state. */ - void clearFixedRotationTransform() { + void finishFixedRotationTransform() { + if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) { + return; + } + final boolean changed = + mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this); + // If it is not the launching app or the display is not rotated, make sure the transform is + // cleared and the configuration is restored from parent. + if (!changed) { + clearFixedRotationTransform(null /* applyDisplayRotation */); + onConfigurationChanged(getParent().getConfiguration()); + } + } + + /** + * Clears the transform and apply display rotation if the action is given. The caller needs to + * refresh the configuration of this container after this method call. + */ + void clearFixedRotationTransform(Runnable applyDisplayRotation) { final FixedRotationTransformState state = mFixedRotationTransformState; - if (state == null || state.mOwner != this) { + if (state == null) { return; } + state.resetTransform(); // Clear the flag so if the display will be updated to the same orientation, the transform - // won't take effect. The state is cleared at the end, because it is used to indicate that - // other windows can use seamless rotation when applying rotation to display. + // won't take effect. state.mIsTransforming = false; - final boolean changed = - mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this); - // If it is not the launching app or the display is not rotated, make sure the merged - // override configuration is restored from parent. - if (!changed) { - onMergedOverrideConfigurationChanged(); + if (applyDisplayRotation != null) { + applyDisplayRotation.run(); } + // The state is cleared at the end, because it is used to indicate that other windows can + // use seamless rotation when applying rotation to display. for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) { state.mAssociatedTokens.get(i).mFixedRotationTransformState = null; } diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e3f9ae8969b3..9bc5d34c11af 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -206,7 +206,7 @@ public: status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); status_t pilferPointers(const sp<IBinder>& token); - void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId); + void displayRemoved(JNIEnv* env, int32_t displayId); void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj); void setFocusedDisplay(JNIEnv* env, int32_t displayId); void setInputDispatchMode(bool enabled, bool frozen); @@ -771,55 +771,10 @@ void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration } } -void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, - int32_t displayId) { - std::vector<sp<InputWindowHandle> > windowHandles; - - if (windowHandleObjArray) { - jsize length = env->GetArrayLength(windowHandleObjArray); - for (jsize i = 0; i < length; i++) { - jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); - if (! windowHandleObj) { - break; // found null element indicating end of used portion of the array - } - - sp<InputWindowHandle> windowHandle = - android_view_InputWindowHandle_getHandle(env, windowHandleObj); - if (windowHandle != nullptr) { - windowHandles.push_back(windowHandle); - } - env->DeleteLocalRef(windowHandleObj); - } - } - - mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId); - - // Do this after the dispatcher has updated the window handle state. - bool newPointerGesturesEnabled = true; - size_t numWindows = windowHandles.size(); - for (size_t i = 0; i < numWindows; i++) { - const sp<InputWindowHandle>& windowHandle = windowHandles[i]; - const InputWindowInfo* windowInfo = windowHandle->getInfo(); - if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures - & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { - newPointerGesturesEnabled = false; - } - } - - bool pointerGesturesEnabledChanged = false; - { // acquire lock - AutoMutex _l(mLock); - - if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { - mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; - pointerGesturesEnabledChanged = true; - } - } // release lock - - if (pointerGesturesEnabledChanged) { - mInputManager->getReader()->requestRefreshConfiguration( - InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT); - } +void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { + // Set an empty list to remove all handles from the specific display. + std::vector<sp<InputWindowHandle>> windowHandles; + mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}}); } void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, @@ -1567,11 +1522,10 @@ static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */, im->getInputManager()->getReader()->toggleCapsLockState(deviceId); } -static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */, - jlong ptr, jobjectArray windowHandleObjArray, jint displayId) { +static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - im->setInputWindows(env, windowHandleObjArray, displayId); + im->displayRemoved(env, displayId); } static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */, @@ -1815,8 +1769,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", (void*)nativeVerifyInputEvent}, {"nativeToggleCapsLock", "(JI)V", (void*)nativeToggleCapsLock}, - {"nativeSetInputWindows", "(J[Landroid/view/InputWindowHandle;I)V", - (void*)nativeSetInputWindows}, + {"nativeDisplayRemoved", "(JI)V", (void*)nativeDisplayRemoved}, {"nativeSetFocusedApplication", "(JILandroid/view/InputApplicationHandle;)V", (void*)nativeSetFocusedApplication}, {"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 09fab3e29fe0..e304bca4553c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -7171,7 +7171,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ActiveAdmin admin; synchronized (getLockObject()) { if (who == null) { - if ((frpManagementAgentUid != mInjector.binderGetCallingUid())) { + if ((frpManagementAgentUid != mInjector.binderGetCallingUid()) + && (mContext.checkCallingPermission(permission.MASTER_CLEAR) + != PackageManager.PERMISSION_GRANTED)) { throw new SecurityException( "Must be called by the FRP management agent on device"); } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b019e9dd03ba..c631eebe4a01 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -144,7 +144,6 @@ import com.android.server.power.ThermalManagerService; import com.android.server.recoverysystem.RecoverySystemService; import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.role.RoleManagerService; -import com.android.server.rollback.RollbackManagerService; import com.android.server.security.FileIntegrityService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; @@ -294,6 +293,8 @@ public final class SystemServer { "com.android.server.DeviceIdleController"; private static final String BLOB_STORE_MANAGER_SERVICE_CLASS = "com.android.server.blob.BlobStoreManagerService"; + private static final String ROLLBACK_MANAGER_SERVICE_CLASS = + "com.android.server.rollback.RollbackManagerService"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -964,7 +965,7 @@ public final class SystemServer { // Manages apk rollbacks. t.traceBegin("StartRollbackManagerService"); - mSystemServiceManager.startService(RollbackManagerService.class); + mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS); t.traceEnd(); // Service to capture bugreports. diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index ae8d5743668a..136ee91dd685 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -26,6 +26,7 @@ import android.app.NotificationManager; import android.app.Person; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; +import android.app.usage.UsageEvents; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -70,6 +71,7 @@ import com.android.server.notification.NotificationManagerInternal; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -237,6 +239,27 @@ public class DataManager { eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType)); } + /** + * Queries events for moving app to foreground between {@code startTime} and {@code endTime}. + */ + @NonNull + public List<UsageEvents.Event> queryAppMovingToForegroundEvents(@UserIdInt int callingUserId, + long startTime, long endTime) { + return UsageStatsQueryHelper.queryAppMovingToForegroundEvents(callingUserId, startTime, + endTime); + } + + /** + * Queries launch counts of apps within {@code packageNameFilter} between {@code startTime} + * and {@code endTime}. + */ + @NonNull + public Map<String, Integer> queryAppLaunchCount(@UserIdInt int callingUserId, long startTime, + long endTime, Set<String> packageNameFilter) { + return UsageStatsQueryHelper.queryAppLaunchCount(callingUserId, startTime, endTime, + packageNameFilter); + } + /** Prunes the data for the specified user. */ public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) { UserData userData = getUnlockedUserData(userId); @@ -382,7 +405,13 @@ public class DataManager { } } - private int mimeTypeToShareEventType(String mimeType) { + /** + * Converts {@code mimeType} to {@link Event.EventType}. + */ + public int mimeTypeToShareEventType(String mimeType) { + if (mimeType == null) { + return Event.TYPE_SHARE_OTHER; + } if (mimeType.startsWith("text/")) { return Event.TYPE_SHARE_TEXT; } else if (mimeType.startsWith("image/")) { diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java index 72f1abb70e34..6e6fea93c803 100644 --- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java @@ -19,6 +19,8 @@ package com.android.server.people.data; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.UsageEvents; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ComponentName; import android.content.LocusId; @@ -27,7 +29,10 @@ import android.util.ArrayMap; import com.android.server.LocalServices; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; /** A helper class that queries {@link UsageStatsManagerInternal}. */ @@ -46,7 +51,7 @@ class UsageStatsQueryHelper { */ UsageStatsQueryHelper(@UserIdInt int userId, Function<String, PackageData> packageDataGetter) { - mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); + mUsageStatsManagerInternal = getUsageStatsManagerInternal(); mUserId = userId; mPackageDataGetter = packageDataGetter; } @@ -106,6 +111,53 @@ class UsageStatsQueryHelper { return mLastEventTimestamp; } + /** + * Queries {@link UsageStatsManagerInternal} events for moving app to foreground between + * {@code startTime} and {@code endTime}. + * + * @return a list containing events moving app to foreground. + */ + static List<UsageEvents.Event> queryAppMovingToForegroundEvents(@UserIdInt int userId, + long startTime, long endTime) { + List<UsageEvents.Event> res = new ArrayList<>(); + UsageEvents usageEvents = getUsageStatsManagerInternal().queryEventsForUser(userId, + startTime, endTime, + UsageEvents.HIDE_SHORTCUT_EVENTS | UsageEvents.HIDE_LOCUS_EVENTS); + if (usageEvents == null) { + return res; + } + while (usageEvents.hasNextEvent()) { + UsageEvents.Event e = new UsageEvents.Event(); + usageEvents.getNextEvent(e); + if (e.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) { + res.add(e); + } + } + return res; + } + + /** + * Queries {@link UsageStatsManagerInternal} for launch count of apps within {@code + * packageNameFilter} between {@code startTime} and {@code endTime}.obfuscateInstantApps + * + * @return a map which keys are package names and values are app launch counts. + */ + static Map<String, Integer> queryAppLaunchCount(@UserIdInt int userId, long startTime, + long endTime, Set<String> packageNameFilter) { + List<UsageStats> stats = getUsageStatsManagerInternal().queryUsageStatsForUser(userId, + UsageStatsManager.INTERVAL_BEST, startTime, endTime, + /* obfuscateInstantApps= */ false); + Map<String, Integer> aggregatedStats = new ArrayMap<>(); + for (UsageStats stat : stats) { + String packageName = stat.getPackageName(); + if (packageNameFilter.contains(packageName)) { + aggregatedStats.put(packageName, + aggregatedStats.getOrDefault(packageName, 0) + stat.getAppLaunchCount()); + } + } + return aggregatedStats; + } + private void onInAppConversationEnded(@NonNull PackageData packageData, @NonNull UsageEvents.Event endEvent) { ComponentName activityName = @@ -138,4 +190,8 @@ class UsageStatsQueryHelper { EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId()); eventHistory.addEvent(event); } + + private static UsageStatsManagerInternal getUsageStatsManagerInternal() { + return LocalServices.getService(UsageStatsManagerInternal.class); + } } diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java index 8e5d75be12b7..d09d0b379769 100644 --- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java +++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java @@ -27,13 +27,11 @@ import android.app.prediction.AppTargetId; import android.content.IntentFilter; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager.ShareShortcutInfo; -import android.util.Range; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ChooserActivity; import com.android.server.people.data.ConversationInfo; import com.android.server.people.data.DataManager; -import com.android.server.people.data.Event; import com.android.server.people.data.EventHistory; import com.android.server.people.data.PackageData; @@ -42,6 +40,9 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; +/** + * Predictor that predicts the {@link AppTarget} the user is most likely to open on share sheet. + */ class ShareTargetPredictor extends AppTargetPredictor { private final IntentFilter mIntentFilter; @@ -66,7 +67,9 @@ class ShareTargetPredictor extends AppTargetPredictor { @Override void predictTargets() { List<ShareTarget> shareTargets = getDirectShareTargets(); - rankTargets(shareTargets); + SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter), + System.currentTimeMillis()); + Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore())); List<AppTarget> res = new ArrayList<>(); for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(), shareTargets.size()); i++) { @@ -80,36 +83,16 @@ class ShareTargetPredictor extends AppTargetPredictor { @Override void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) { List<ShareTarget> shareTargets = getAppShareTargets(targets); - rankTargets(shareTargets); + SharesheetModelScorer.computeScoreForAppShare(shareTargets, + getShareEventType(mIntentFilter), getPredictionContext().getPredictedTargetCount(), + System.currentTimeMillis(), getDataManager(), + mCallingUserId); + Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore())); List<AppTarget> appTargetList = new ArrayList<>(); shareTargets.forEach(t -> appTargetList.add(t.getAppTarget())); callback.accept(appTargetList); } - private void rankTargets(List<ShareTarget> shareTargets) { - // Rank targets based on recency of sharing history only for the moment. - // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app. - Collections.sort(shareTargets, (t1, t2) -> { - if (t1.getEventHistory() == null) { - return 1; - } - if (t2.getEventHistory() == null) { - return -1; - } - Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex( - Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); - Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex( - Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); - if (timeSlot1 == null) { - return 1; - } else if (timeSlot2 == null) { - return -1; - } else { - return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper()); - } - }); - } - private List<ShareTarget> getDirectShareTargets() { List<ShareTarget> shareTargets = new ArrayList<>(); List<ShareShortcutInfo> shareShortcuts = @@ -153,6 +136,11 @@ class ShareTargetPredictor extends AppTargetPredictor { return shareTargets; } + private int getShareEventType(IntentFilter intentFilter) { + String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null; + return getDataManager().mimeTypeToShareEventType(mimeType); + } + @VisibleForTesting static class ShareTarget { @@ -162,13 +150,16 @@ class ShareTargetPredictor extends AppTargetPredictor { private final EventHistory mEventHistory; @Nullable private final ConversationInfo mConversationInfo; + private float mScore; - private ShareTarget(@NonNull AppTarget appTarget, + @VisibleForTesting + ShareTarget(@NonNull AppTarget appTarget, @Nullable EventHistory eventHistory, @Nullable ConversationInfo conversationInfo) { mAppTarget = appTarget; mEventHistory = eventHistory; mConversationInfo = conversationInfo; + mScore = 0f; } @NonNull @@ -188,5 +179,15 @@ class ShareTargetPredictor extends AppTargetPredictor { ConversationInfo getConversationInfo() { return mConversationInfo; } + + @VisibleForTesting + float getScore() { + return mScore; + } + + @VisibleForTesting + void setScore(float score) { + mScore = score; + } } } diff --git a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java new file mode 100644 index 000000000000..0ac5724210da --- /dev/null +++ b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.people.prediction; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.usage.UsageEvents; +import android.util.ArrayMap; +import android.util.Pair; +import android.util.Range; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.app.ChooserActivity; +import com.android.server.people.data.DataManager; +import com.android.server.people.data.Event; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.concurrent.TimeUnit; + +/** Ranking scorer for Sharesheet targets. */ +class SharesheetModelScorer { + + private static final String TAG = "SharesheetModelScorer"; + private static final boolean DEBUG = false; + private static final Integer RECENCY_SCORE_COUNT = 6; + private static final float RECENCY_INITIAL_BASE_SCORE = 0.4F; + private static final float RECENCY_SCORE_INITIAL_DECAY = 0.05F; + private static final float RECENCY_SCORE_SUBSEQUENT_DECAY = 0.02F; + private static final long ONE_MONTH_WINDOW = TimeUnit.DAYS.toMillis(30); + private static final long FOREGROUND_APP_PROMO_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10); + private static final float FREQUENTLY_USED_APP_SCORE_DECAY = 0.9F; + @VisibleForTesting + static final float FOREGROUND_APP_WEIGHT = 0F; + @VisibleForTesting + static final String CHOOSER_ACTIVITY = ChooserActivity.class.getSimpleName(); + + // Keep constructor private to avoid class being instantiated. + private SharesheetModelScorer() { + } + + /** + * Computes each target's recency, frequency and frequency of the same {@code shareEventType} + * based on past sharing history. Update {@link ShareTargetPredictor.ShareTargetScore}. + */ + static void computeScore(List<ShareTargetPredictor.ShareTarget> shareTargets, + int shareEventType, long now) { + if (shareTargets.isEmpty()) { + return; + } + float totalFreqScore = 0f; + int freqScoreCount = 0; + float totalMimeFreqScore = 0f; + int mimeFreqScoreCount = 0; + // Top of this heap has lowest rank. + PriorityQueue<Pair<ShareTargetRankingScore, Range<Long>>> recencyMinHeap = + new PriorityQueue<>(RECENCY_SCORE_COUNT, + Comparator.comparingLong(p -> p.second.getUpper())); + List<ShareTargetRankingScore> scoreList = new ArrayList<>(shareTargets.size()); + for (ShareTargetPredictor.ShareTarget target : shareTargets) { + ShareTargetRankingScore shareTargetScore = new ShareTargetRankingScore(); + scoreList.add(shareTargetScore); + if (target.getEventHistory() == null) { + continue; + } + // Counts frequency + List<Range<Long>> timeSlots = target.getEventHistory().getEventIndex( + Event.SHARE_EVENT_TYPES).getActiveTimeSlots(); + if (!timeSlots.isEmpty()) { + for (Range<Long> timeSlot : timeSlots) { + shareTargetScore.incrementFrequencyScore( + getFreqDecayedOnElapsedTime(now - timeSlot.getLower())); + } + totalFreqScore += shareTargetScore.getFrequencyScore(); + freqScoreCount++; + } + // Counts frequency for sharing same mime type + List<Range<Long>> timeSlotsOfSameType = target.getEventHistory().getEventIndex( + shareEventType).getActiveTimeSlots(); + if (!timeSlotsOfSameType.isEmpty()) { + for (Range<Long> timeSlot : timeSlotsOfSameType) { + shareTargetScore.incrementMimeFrequencyScore( + getFreqDecayedOnElapsedTime(now - timeSlot.getLower())); + } + totalMimeFreqScore += shareTargetScore.getMimeFrequencyScore(); + mimeFreqScoreCount++; + } + // Records most recent targets + Range<Long> mostRecentTimeSlot = target.getEventHistory().getEventIndex( + Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot(); + if (mostRecentTimeSlot == null) { + continue; + } + if (recencyMinHeap.size() < RECENCY_SCORE_COUNT + || mostRecentTimeSlot.getUpper() > recencyMinHeap.peek().second.getUpper()) { + if (recencyMinHeap.size() == RECENCY_SCORE_COUNT) { + recencyMinHeap.poll(); + } + recencyMinHeap.offer(new Pair(shareTargetScore, mostRecentTimeSlot)); + } + } + // Calculates recency score + while (!recencyMinHeap.isEmpty()) { + float recencyScore = RECENCY_INITIAL_BASE_SCORE; + if (recencyMinHeap.size() > 1) { + recencyScore = RECENCY_INITIAL_BASE_SCORE - RECENCY_SCORE_INITIAL_DECAY + - RECENCY_SCORE_SUBSEQUENT_DECAY * (recencyMinHeap.size() - 2); + } + recencyMinHeap.poll().first.setRecencyScore(recencyScore); + } + + Float avgFreq = freqScoreCount != 0 ? totalFreqScore / freqScoreCount : 0f; + Float avgMimeFreq = mimeFreqScoreCount != 0 ? totalMimeFreqScore / mimeFreqScoreCount : 0f; + for (int i = 0; i < scoreList.size(); i++) { + ShareTargetPredictor.ShareTarget target = shareTargets.get(i); + ShareTargetRankingScore targetScore = scoreList.get(i); + // Normalizes freq and mimeFreq score + targetScore.setFrequencyScore(normalizeFreqScore( + avgFreq.equals(0f) ? 0f : targetScore.getFrequencyScore() / avgFreq)); + targetScore.setMimeFrequencyScore(normalizeMimeFreqScore(avgMimeFreq.equals(0f) ? 0f + : targetScore.getMimeFrequencyScore() / avgMimeFreq)); + // Calculates total score + targetScore.setTotalScore( + probOR(probOR(targetScore.getRecencyScore(), targetScore.getFrequencyScore()), + targetScore.getMimeFrequencyScore())); + target.setScore(targetScore.getTotalScore()); + + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: packageName: %s, className: %s, shortcutId: %s, " + + "recency:%.2f, freq_all:%.2f, freq_mime:%.2f, total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getAppTarget().getShortcutInfo() != null + ? target.getAppTarget().getShortcutInfo().getId() : null, + targetScore.getRecencyScore(), + targetScore.getFrequencyScore(), + targetScore.getMimeFrequencyScore(), + targetScore.getTotalScore())); + } + } + } + + /** + * Computes ranking score for app sharing. Update {@link ShareTargetPredictor.ShareTargetScore}. + */ + static void computeScoreForAppShare(List<ShareTargetPredictor.ShareTarget> shareTargets, + int shareEventType, int targetsLimit, long now, @NonNull DataManager dataManager, + @UserIdInt int callingUserId) { + computeScore(shareTargets, shareEventType, now); + postProcess(shareTargets, targetsLimit, dataManager, callingUserId); + } + + private static void postProcess(List<ShareTargetPredictor.ShareTarget> shareTargets, + int targetsLimit, @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + // Populates a map which key is package name and value is list of shareTargets descended + // on total score. + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap = new ArrayMap<>(); + for (ShareTargetPredictor.ShareTarget shareTarget : shareTargets) { + String packageName = shareTarget.getAppTarget().getPackageName(); + shareTargetMap.computeIfAbsent(packageName, key -> new ArrayList<>()); + List<ShareTargetPredictor.ShareTarget> targetsList = shareTargetMap.get(packageName); + int index = 0; + while (index < targetsList.size()) { + if (shareTarget.getScore() > targetsList.get(index).getScore()) { + break; + } + index++; + } + targetsList.add(index, shareTarget); + } + promoteForegroundApp(shareTargetMap, dataManager, callingUserId); + promoteFrequentlyUsedApps(shareTargetMap, targetsLimit, dataManager, callingUserId); + } + + /** + * Promotes frequently used sharing apps, if recommended apps based on sharing history have not + * reached the limit (e.g. user did not share any content in last couple weeks) + */ + private static void promoteFrequentlyUsedApps( + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, int targetsLimit, + @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + int validPredictionNum = 0; + float minValidScore = 1f; + for (List<ShareTargetPredictor.ShareTarget> targets : shareTargetMap.values()) { + for (ShareTargetPredictor.ShareTarget target : targets) { + if (target.getScore() > 0f) { + validPredictionNum++; + minValidScore = Math.min(target.getScore(), minValidScore); + } + } + } + // Skips if recommended apps based on sharing history have already reached the limit. + if (validPredictionNum >= targetsLimit) { + return; + } + long now = System.currentTimeMillis(); + Map<String, Integer> appLaunchCountsMap = dataManager.queryAppLaunchCount( + callingUserId, now - ONE_MONTH_WINDOW, now, shareTargetMap.keySet()); + List<Pair<String, Integer>> appLaunchCounts = new ArrayList<>(); + for (Map.Entry<String, Integer> entry : appLaunchCountsMap.entrySet()) { + if (entry.getValue() > 0) { + appLaunchCounts.add(new Pair(entry.getKey(), entry.getValue())); + } + } + Collections.sort(appLaunchCounts, (p1, p2) -> -Integer.compare(p1.second, p2.second)); + for (Pair<String, Integer> entry : appLaunchCounts) { + if (!shareTargetMap.containsKey(entry.first)) { + continue; + } + ShareTargetPredictor.ShareTarget target = shareTargetMap.get(entry.first).get(0); + if (target.getScore() > 0f) { + continue; + } + minValidScore *= FREQUENTLY_USED_APP_SCORE_DECAY; + target.setScore(minValidScore); + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: promoteFrequentUsedApps packageName: %s, className: %s," + + " total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getScore())); + } + validPredictionNum++; + if (validPredictionNum == targetsLimit) { + return; + } + } + } + + /** + * Promotes the foreground app just prior to source sharing app. Share often occurs between + * two apps the user is switching. + */ + private static void promoteForegroundApp( + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, + @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + String sharingForegroundApp = findSharingForegroundApp(shareTargetMap, dataManager, + callingUserId); + if (sharingForegroundApp != null) { + ShareTargetPredictor.ShareTarget target = shareTargetMap.get(sharingForegroundApp).get( + 0); + target.setScore(probOR(target.getScore(), FOREGROUND_APP_WEIGHT)); + if (DEBUG) { + Slog.d(TAG, String.format( + "SharesheetModel: promoteForegroundApp packageName: %s, className: %s, " + + "total:%.2f", + target.getAppTarget().getPackageName(), + target.getAppTarget().getClassName(), + target.getScore())); + } + } + } + + /** + * Find the foreground app just prior to source sharing app from usageStatsManager. Returns null + * if it is not available. + */ + @Nullable + private static String findSharingForegroundApp( + Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, + @NonNull DataManager dataManager, @UserIdInt int callingUserId) { + String sharingForegroundApp = null; + long now = System.currentTimeMillis(); + List<UsageEvents.Event> events = dataManager.queryAppMovingToForegroundEvents( + callingUserId, now - FOREGROUND_APP_PROMO_TIME_WINDOW, now); + String sourceApp = null; + for (int i = events.size() - 1; i >= 0; i--) { + String className = events.get(i).getClassName(); + String packageName = events.get(i).getPackageName(); + if (packageName == null || (className != null && className.contains(CHOOSER_ACTIVITY)) + || packageName.contains(CHOOSER_ACTIVITY)) { + continue; + } + if (sourceApp == null) { + sourceApp = packageName; + } else if (!packageName.equals(sourceApp) && shareTargetMap.containsKey(packageName)) { + sharingForegroundApp = packageName; + break; + } + } + return sharingForegroundApp; + } + + /** + * Probabilistic OR (also known as the algebraic sum). If a <= 1 and b <= 1, the result will be + * <= 1.0. + */ + private static float probOR(float a, float b) { + return 1f - (1f - a) * (1f - b); + } + + /** Counts frequency of share targets. Decays frequency for old shares. */ + private static float getFreqDecayedOnElapsedTime(long elapsedTimeMillis) { + Duration duration = Duration.ofMillis(elapsedTimeMillis); + if (duration.compareTo(Duration.ofDays(1)) <= 0) { + return 1.0f; + } else if (duration.compareTo(Duration.ofDays(3)) <= 0) { + return 0.9f; + } else if (duration.compareTo(Duration.ofDays(7)) <= 0) { + return 0.8f; + } else if (duration.compareTo(Duration.ofDays(14)) <= 0) { + return 0.7f; + } else { + return 0.6f; + } + } + + /** Normalizes frequency score. */ + private static float normalizeFreqScore(double freqRatio) { + if (freqRatio >= 2.5) { + return 0.2f; + } else if (freqRatio >= 1.5) { + return 0.15f; + } else if (freqRatio >= 1.0) { + return 0.1f; + } else if (freqRatio >= 0.75) { + return 0.05f; + } else { + return 0f; + } + } + + /** Normalizes mimetype-specific frequency score. */ + private static float normalizeMimeFreqScore(double freqRatio) { + if (freqRatio >= 2.0) { + return 0.2f; + } else if (freqRatio >= 1.2) { + return 0.15f; + } else if (freqRatio > 0.0) { + return 0.1f; + } else { + return 0f; + } + } + + private static class ShareTargetRankingScore { + + private float mRecencyScore = 0f; + private float mFrequencyScore = 0f; + private float mMimeFrequencyScore = 0f; + private float mTotalScore = 0f; + + float getTotalScore() { + return mTotalScore; + } + + void setTotalScore(float totalScore) { + mTotalScore = totalScore; + } + + float getRecencyScore() { + return mRecencyScore; + } + + void setRecencyScore(float recencyScore) { + mRecencyScore = recencyScore; + } + + float getFrequencyScore() { + return mFrequencyScore; + } + + void setFrequencyScore(float frequencyScore) { + mFrequencyScore = frequencyScore; + } + + void incrementFrequencyScore(float incremental) { + mFrequencyScore += incremental; + } + + float getMimeFrequencyScore() { + return mMimeFrequencyScore; + } + + void setMimeFrequencyScore(float mimeFrequencyScore) { + mMimeFrequencyScore = mimeFrequencyScore; + } + + void incrementMimeFrequencyScore(float incremental) { + mMimeFrequencyScore += incremental; + } + } +} diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java index acdb68142178..138f9829c088 100644 --- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java +++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java @@ -17,6 +17,7 @@ package com.android.server.pm; import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND; @@ -104,6 +105,7 @@ public class CrossProfileAppsServiceImplRoboTest { private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mInjector); private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>(); + private final Map<Integer, List<ApplicationInfo>> installedApplications = new HashMap<>(); @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private IPackageManager mIPackageManager; @@ -112,12 +114,18 @@ public class CrossProfileAppsServiceImplRoboTest { @Before public void initializeMocks() throws Exception { MockitoAnnotations.initMocks(this); + initializeInstalledApplicationsMock(); mockCrossProfileAppInstalledAndEnabledOnEachProfile(); mockCrossProfileAppRequestsInteractAcrossProfiles(); mockCrossProfileAppRegistersBroadcastReceiver(); mockCrossProfileAppWhitelisted(); } + private void initializeInstalledApplicationsMock() { + when(mPackageManagerInternal.getInstalledApplications(anyInt(), anyInt(), eq(CALLING_UID))) + .thenAnswer(invocation -> installedApplications.get(invocation.getArgument(1))); + } + private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() { // They are enabled by default, so we simply have to ensure that a package info with an // application info is returned. @@ -138,11 +146,14 @@ public class CrossProfileAppsServiceImplRoboTest { when(mPackageManagerInternal.getPackage(uid)) .thenReturn(((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME) .hideAsParsed()).hideAsFinal()); + installedApplications.putIfAbsent(userId, new ArrayList<>()); + installedApplications.get(userId).add(packageInfo.applicationInfo); } private PackageInfo buildTestPackageInfo() { PackageInfo packageInfo = new PackageInfo(); packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME; return packageInfo; } @@ -451,6 +462,13 @@ public class CrossProfileAppsServiceImplRoboTest { .isTrue(); } + @Test + public void clearInteractAcrossProfilesAppOps() { + explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED); + mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps(); + assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT); + } + private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) { explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode); } 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 3ad905476190..d2925263125d 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -64,10 +64,13 @@ import com.android.internal.statusbar.IStatusBarService; import org.junit.Before; import org.junit.Test; +import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Random; + @SmallTest public class BiometricServiceTest { @@ -347,9 +350,19 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_happyPathWithoutConfirmation() throws Exception { + public void testAuthenticate_happyPathWithoutConfirmation_strongBiometric() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG); + testAuthenticate_happyPathWithoutConfirmation(true /* isStrongBiometric */); + } + + @Test + public void testAuthenticate_happyPathWithoutConfirmation_weakBiometric() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK); + testAuthenticate_happyPathWithoutConfirmation(false /* isStrongBiometric */); + } + private void testAuthenticate_happyPathWithoutConfirmation(boolean isStrongBiometric) + throws Exception { // Start testing the happy path invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */); @@ -397,9 +410,11 @@ public class BiometricServiceTest { anyLong() /* sessionId */); // Hardware authenticated + final byte[] HAT = generateRandomHAT(); mBiometricService.mInternalReceiver.onAuthenticationSucceeded( false /* requireConfirmation */, - new byte[69] /* HAT */); + HAT, + isStrongBiometric /* isStrongBiometric */); waitForIdle(); // Waiting for SystemUI to send dismissed callback assertEquals(mBiometricService.mCurrentAuthSession.mState, @@ -413,7 +428,11 @@ public class BiometricServiceTest { null /* credentialAttestation */); waitForIdle(); // HAT sent to keystore - verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); + if (isStrongBiometric) { + verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT)); + } else { + verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); + } // Send onAuthenticated to client verify(mReceiver1).onAuthenticationSucceeded( BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC); @@ -447,16 +466,29 @@ public class BiometricServiceTest { } @Test - public void testAuthenticate_happyPathWithConfirmation() throws Exception { + public void testAuthenticate_happyPathWithConfirmation_strongBiometric() throws Exception { setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + testAuthenticate_happyPathWithConfirmation(true /* isStrongBiometric */); + } + + @Test + public void testAuthenticate_happyPathWithConfirmation_weakBiometric() throws Exception { + setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK); + testAuthenticate_happyPathWithConfirmation(false /* isStrongBiometric */); + } + + private void testAuthenticate_happyPathWithConfirmation(boolean isStrongBiometric) + throws Exception { invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1, true /* requireConfirmation */, null /* authenticators */); // Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not // sent to KeyStore yet + final byte[] HAT = generateRandomHAT(); mBiometricService.mInternalReceiver.onAuthenticationSucceeded( true /* requireConfirmation */, - new byte[69] /* HAT */); + HAT, + isStrongBiometric /* isStrongBiometric */); waitForIdle(); // Waiting for SystemUI to send confirmation callback assertEquals(mBiometricService.mCurrentAuthSession.mState, @@ -468,7 +500,11 @@ public class BiometricServiceTest { BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED, null /* credentialAttestation */); waitForIdle(); - verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class)); + if (isStrongBiometric) { + verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT)); + } else { + verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); + } verify(mReceiver1).onAuthenticationSucceeded( BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC); } @@ -909,7 +945,8 @@ public class BiometricServiceTest { mBiometricService.mInternalReceiver.onAuthenticationSucceeded( true /* requireConfirmation */, - new byte[69] /* HAT */); + new byte[69] /* HAT */, + true /* isStrongBiometric */); mBiometricService.mInternalReceiver.onDialogDismissed( BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */); waitForIdle(); @@ -927,6 +964,7 @@ public class BiometricServiceTest { eq(BiometricAuthenticator.TYPE_FACE), eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED), eq(0 /* vendorCode */)); + verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class)); assertNull(mBiometricService.mCurrentAuthSession); } @@ -1238,20 +1276,6 @@ public class BiometricServiceTest { mFingerprintAuthenticator); } - @Test(expected = IllegalStateException.class) - public void testRegistrationWithUnsupportedStrength_throwsIllegalStateException() - throws Exception { - mBiometricService = new BiometricService(mContext, mInjector); - mBiometricService.onStart(); - - // Only STRONG and WEAK are supported. Let's enforce that CONVENIENCE cannot be - // registered. If there is a compelling reason, we can remove this constraint. - mBiometricService.mImpl.registerAuthenticator( - 0 /* id */, 2 /* modality */, - Authenticators.BIOMETRIC_CONVENIENCE /* strength */, - mFingerprintAuthenticator); - } - @Test(expected = IllegalArgumentException.class) public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException() throws Exception { @@ -1508,4 +1532,13 @@ public class BiometricServiceTest { private static void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } + + private byte[] generateRandomHAT() { + byte[] HAT = new byte[69]; + Random random = new Random(); + random.nextBytes(HAT); + return HAT; + } + + } diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index 7934d33f907d..03d9ad51e6c5 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -21,15 +21,16 @@ import static com.android.server.people.data.TestUtils.timestamp; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.usage.UsageEvents; +import android.app.usage.UsageStats; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.LocusId; @@ -50,6 +51,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.function.Predicate; @@ -58,7 +60,8 @@ import java.util.function.Predicate; public final class UsageStatsQueryHelperTest { private static final int USER_ID_PRIMARY = 0; - private static final String PKG_NAME = "pkg"; + private static final String PKG_NAME_1 = "pkg_1"; + private static final String PKG_NAME_2 = "pkg_2"; private static final String ACTIVITY_NAME = "TestActivity"; private static final String SHORTCUT_ID = "abc"; private static final LocusId LOCUS_ID_1 = new LocusId("locus_1"); @@ -80,7 +83,7 @@ public final class UsageStatsQueryHelperTest { File testDir = new File(ctx.getCacheDir(), "testdir"); ScheduledExecutorService scheduledExecutorService = new MockScheduledExecutorService(); - mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false, + mPackageData = new TestPackageData(PKG_NAME_1, USER_ID_PRIMARY, pkg -> false, pkg -> false, scheduledExecutorService, testDir); mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder() .setShortcutId(SHORTCUT_ID) @@ -173,10 +176,72 @@ public final class UsageStatsQueryHelperTest { assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2)); } + @Test + public void testQueryAppMovingToForegroundEvents() { + addUsageEvents( + createShortcutInvocationEvent(100_000L), + createActivityResumedEvent(110_000L), + createActivityStoppedEvent(120_000L), + createActivityResumedEvent(130_000L)); + + List<UsageEvents.Event> events = mHelper.queryAppMovingToForegroundEvents(USER_ID_PRIMARY, + 90_000L, + 200_000L); + + assertEquals(2, events.size()); + assertEquals(UsageEvents.Event.ACTIVITY_RESUMED, events.get(0).getEventType()); + assertEquals(110_000L, events.get(0).getTimeStamp()); + assertEquals(UsageEvents.Event.ACTIVITY_RESUMED, events.get(1).getEventType()); + assertEquals(130_000L, events.get(1).getTimeStamp()); + } + + @Test + public void testQueryAppLaunchCount() { + + UsageStats packageStats1 = createUsageStats(PKG_NAME_1, 2); + UsageStats packageStats2 = createUsageStats(PKG_NAME_1, 3); + UsageStats packageStats3 = createUsageStats(PKG_NAME_2, 1); + when(mUsageStatsManagerInternal.queryUsageStatsForUser(anyInt(), anyInt(), anyLong(), + anyLong(), anyBoolean())).thenReturn( + List.of(packageStats1, packageStats2, packageStats3)); + + Map<String, Integer> appLaunchCounts = mHelper.queryAppLaunchCount(USER_ID_PRIMARY, 90_000L, + 200_000L, Set.of(PKG_NAME_1, PKG_NAME_2)); + + assertEquals(2, appLaunchCounts.size()); + assertEquals(5, (long) appLaunchCounts.get(PKG_NAME_1)); + assertEquals(1, (long) appLaunchCounts.get(PKG_NAME_2)); + } + + @Test + public void testQueryAppLaunchCount_packageNameFiltered() { + + UsageStats packageStats1 = createUsageStats(PKG_NAME_1, 2); + UsageStats packageStats2 = createUsageStats(PKG_NAME_1, 3); + UsageStats packageStats3 = createUsageStats(PKG_NAME_2, 1); + when(mUsageStatsManagerInternal.queryUsageStatsForUser(anyInt(), anyInt(), anyLong(), + anyLong(), anyBoolean())).thenReturn( + List.of(packageStats1, packageStats2, packageStats3)); + + Map<String, Integer> appLaunchCounts = mHelper.queryAppLaunchCount(USER_ID_PRIMARY, 90_000L, + 200_000L, + Set.of(PKG_NAME_1)); + + assertEquals(1, appLaunchCounts.size()); + assertEquals(5, (long) appLaunchCounts.get(PKG_NAME_1)); + } + private void addUsageEvents(UsageEvents.Event... events) { UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{}); when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(), - eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents); + anyInt())).thenReturn(usageEvents); + } + + private static UsageStats createUsageStats(String packageName, int launchCount) { + UsageStats packageStats = new UsageStats(); + packageStats.mPackageName = packageName; + packageStats.mAppLaunchCount = launchCount; + return packageStats; } private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { @@ -203,9 +268,15 @@ public final class UsageStatsQueryHelperTest { return e; } + private static UsageEvents.Event createActivityResumedEvent(long timestamp) { + UsageEvents.Event e = createUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, timestamp); + e.mClass = ACTIVITY_NAME; + return e; + } + private static UsageEvents.Event createUsageEvent(int eventType, long timestamp) { UsageEvents.Event e = new UsageEvents.Event(eventType, timestamp); - e.mPackage = PKG_NAME; + e.mPackage = PKG_NAME_1; return e; } diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java index c6cd34732acf..1480627b9b9f 100644 --- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java +++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java @@ -127,6 +127,9 @@ public final class ShareTargetPredictorTest { when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3); when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); @@ -183,6 +186,12 @@ public final class ShareTargetPredictorTest { when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6); + when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anyInt())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anyInt())).thenReturn(mEventIndex5); + when(mEventHistory6.getEventIndex(anyInt())).thenReturn(mEventIndex6); when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); @@ -220,19 +229,19 @@ public final class ShareTargetPredictorTest { @Test public void testSortTargets() { AppTarget appTarget1 = new AppTarget.Builder( - new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) .setClassName(CLASS_1) .build(); AppTarget appTarget2 = new AppTarget.Builder( - new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) .setClassName(CLASS_2) .build(); AppTarget appTarget3 = new AppTarget.Builder( - new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) .setClassName(CLASS_1) .build(); AppTarget appTarget4 = new AppTarget.Builder( - new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) .setClassName(CLASS_2) .build(); AppTarget appTarget5 = new AppTarget.Builder( @@ -251,6 +260,10 @@ public final class ShareTargetPredictorTest { when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anyInt())).thenReturn(mEventIndex4); when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L)); when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L)); when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L)); @@ -265,14 +278,14 @@ public final class ShareTargetPredictorTest { appTarget4, appTarget3, appTarget2, appTarget1, appTarget5); } - private ShareShortcutInfo buildShareShortcut( + private static ShareShortcutInfo buildShareShortcut( String packageName, String className, String shortcutId) { ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId); ComponentName componentName = new ComponentName(packageName, className); return new ShareShortcutInfo(shortcutInfo, componentName); } - private ShortcutInfo buildShortcut(String packageName, String shortcutId) { + private static ShortcutInfo buildShortcut(String packageName, String shortcutId) { Context mockContext = mock(Context.class); when(mockContext.getPackageName()).thenReturn(packageName); when(mockContext.getUserId()).thenReturn(USER_ID); diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java new file mode 100644 index 000000000000..9d96d6b7d861 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.people.prediction; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.prediction.AppTarget; +import android.app.prediction.AppTargetId; +import android.app.usage.UsageEvents; +import android.os.UserHandle; +import android.util.Range; + +import com.android.server.people.data.DataManager; +import com.android.server.people.data.Event; +import com.android.server.people.data.EventHistory; +import com.android.server.people.data.EventIndex; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.time.Duration; +import java.util.List; +import java.util.Map; + +@RunWith(JUnit4.class) +public final class SharesheetModelScorerTest { + + private static final int USER_ID = 0; + private static final String PACKAGE_1 = "pkg1"; + private static final String PACKAGE_2 = "pkg2"; + private static final String PACKAGE_3 = "pkg3"; + private static final String CLASS_1 = "cls1"; + private static final String CLASS_2 = "cls2"; + private static final double DELTA = 1e-6; + private static final long NOW = System.currentTimeMillis(); + private static final Range<Long> WITHIN_ONE_DAY = new Range( + NOW - Duration.ofHours(23).toMillis(), + NOW - Duration.ofHours(22).toMillis()); + private static final Range<Long> TWO_DAYS_AGO = new Range( + NOW - Duration.ofHours(50).toMillis(), + NOW - Duration.ofHours(49).toMillis()); + private static final Range<Long> FIVE_DAYS_AGO = new Range( + NOW - Duration.ofDays(6).toMillis(), + NOW - Duration.ofDays(5).toMillis()); + private static final Range<Long> EIGHT_DAYS_AGO = new Range( + NOW - Duration.ofDays(9).toMillis(), + NOW - Duration.ofDays(8).toMillis()); + private static final Range<Long> TWELVE_DAYS_AGO = new Range( + NOW - Duration.ofDays(13).toMillis(), + NOW - Duration.ofDays(12).toMillis()); + private static final Range<Long> TWENTY_DAYS_AGO = new Range( + NOW - Duration.ofDays(21).toMillis(), + NOW - Duration.ofDays(20).toMillis()); + private static final Range<Long> FOUR_WEEKS_AGO = new Range( + NOW - Duration.ofDays(29).toMillis(), + NOW - Duration.ofDays(28).toMillis()); + + @Mock + private DataManager mDataManager; + @Mock + private EventHistory mEventHistory1; + @Mock + private EventHistory mEventHistory2; + @Mock + private EventHistory mEventHistory3; + @Mock + private EventHistory mEventHistory4; + @Mock + private EventHistory mEventHistory5; + @Mock + private EventIndex mEventIndex1; + @Mock + private EventIndex mEventIndex2; + @Mock + private EventIndex mEventIndex3; + @Mock + private EventIndex mEventIndex4; + @Mock + private EventIndex mEventIndex5; + @Mock + private EventIndex mEventIndex6; + @Mock + private EventIndex mEventIndex7; + @Mock + private EventIndex mEventIndex8; + @Mock + private EventIndex mEventIndex9; + @Mock + private EventIndex mEventIndex10; + + private ShareTargetPredictor.ShareTarget mShareTarget1; + private ShareTargetPredictor.ShareTarget mShareTarget2; + private ShareTargetPredictor.ShareTarget mShareTarget3; + private ShareTargetPredictor.ShareTarget mShareTarget4; + private ShareTargetPredictor.ShareTarget mShareTarget5; + private ShareTargetPredictor.ShareTarget mShareTarget6; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mShareTarget1 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID)) + .setClassName(CLASS_1).build(), + mEventHistory1, null); + mShareTarget2 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder(new AppTargetId("cls2#pkg1"), PACKAGE_1, + UserHandle.of(USER_ID)).setClassName(CLASS_2).build(), + mEventHistory2, null); + mShareTarget3 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + .setClassName(CLASS_1).build(), + mEventHistory3, null); + mShareTarget4 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID)) + .setClassName(CLASS_2).build(), + mEventHistory4, null); + mShareTarget5 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID)) + .setClassName(CLASS_1).build(), + mEventHistory5, null); + mShareTarget6 = new ShareTargetPredictor.ShareTarget( + new AppTarget.Builder( + new AppTargetId("cls2#pkg3"), PACKAGE_3, UserHandle.of(USER_ID)) + .setClassName(CLASS_2).build(), + null, null); + } + + @Test + public void testComputeScore() { + // Frequency and recency + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + + when(mEventIndex1.getActiveTimeSlots()).thenReturn( + List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO)); + when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO)); + when(mEventIndex4.getActiveTimeSlots()).thenReturn( + List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO)); + when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of()); + + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + + // Frequency of the same mime type + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + + when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO)); + when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of()); + when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO)); + when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of()); + + SharesheetModelScorer.computeScore( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, + NOW); + + // Verification + assertEquals(0.514f, mShareTarget1.getScore(), DELTA); + assertEquals(0.475125f, mShareTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareTarget3.getScore(), DELTA); + assertEquals(0.4411f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare() { + // Frequency and recency + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + + when(mEventIndex1.getActiveTimeSlots()).thenReturn( + List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO)); + when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO)); + when(mEventIndex4.getActiveTimeSlots()).thenReturn( + List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO)); + when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of()); + + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + + // Frequency of the same mime type + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + + when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO)); + when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO)); + when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of()); + when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO)); + when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of()); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + // Verification + assertEquals(0.514f, mShareTarget1.getScore(), DELTA); + assertEquals(0.475125f, mShareTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareTarget3.getScore(), DELTA); + assertEquals(0.4411f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_promoteFrequentlyUsedApps() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mDataManager.queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet())) + .thenReturn( + Map.of(PACKAGE_1, 1, + PACKAGE_2, 2, + PACKAGE_3, 3)); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + verify(mDataManager, times(1)).queryAppLaunchCount(anyInt(), anyLong(), anyLong(), + anySet()); + assertEquals(0.9f, mShareTarget5.getScore(), DELTA); + assertEquals(0.81f, mShareTarget3.getScore(), DELTA); + assertEquals(0.729f, mShareTarget1.getScore(), DELTA); + assertEquals(0f, mShareTarget2.getScore(), DELTA); + assertEquals(0f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_skipPromoteFrequentlyUsedAppsWhenReachesLimit() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY); + when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO); + when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO); + when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO); + when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null); + when(mDataManager.queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet())) + .thenReturn( + Map.of(PACKAGE_1, 1, + PACKAGE_2, 2, + PACKAGE_3, 3)); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 4, NOW, mDataManager, USER_ID); + + verify(mDataManager, never()).queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet()); + assertEquals(0.4f, mShareTarget1.getScore(), DELTA); + assertEquals(0.35f, mShareTarget2.getScore(), DELTA); + assertEquals(0.33f, mShareTarget3.getScore(), DELTA); + assertEquals(0.31f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_promoteForegroundApp() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mDataManager.queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong())).thenReturn( + List.of(createUsageEvent(PACKAGE_2), + createUsageEvent(PACKAGE_3), + createUsageEvent(SharesheetModelScorer.CHOOSER_ACTIVITY), + createUsageEvent(PACKAGE_3), + createUsageEvent(PACKAGE_3)) + ); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + verify(mDataManager, times(1)).queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong()); + assertEquals(0f, mShareTarget1.getScore(), DELTA); + assertEquals(0f, mShareTarget2.getScore(), DELTA); + assertEquals(SharesheetModelScorer.FOREGROUND_APP_WEIGHT, mShareTarget3.getScore(), DELTA); + assertEquals(0f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + @Test + public void testComputeScoreForAppShare_skipPromoteForegroundAppWhenNoValidForegroundApp() { + when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1); + when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2); + when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3); + when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4); + when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5); + when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6); + when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7); + when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8); + when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9); + when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10); + when(mDataManager.queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong())).thenReturn( + List.of(createUsageEvent(PACKAGE_3), + createUsageEvent(PACKAGE_3), + createUsageEvent(SharesheetModelScorer.CHOOSER_ACTIVITY), + createUsageEvent(PACKAGE_3), + createUsageEvent(PACKAGE_3)) + ); + + SharesheetModelScorer.computeScoreForAppShare( + List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5, + mShareTarget6), + Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID); + + verify(mDataManager, times(1)).queryAppMovingToForegroundEvents(anyInt(), anyLong(), + anyLong()); + assertEquals(0f, mShareTarget1.getScore(), DELTA); + assertEquals(0f, mShareTarget2.getScore(), DELTA); + assertEquals(0f, mShareTarget3.getScore(), DELTA); + assertEquals(0f, mShareTarget4.getScore(), DELTA); + assertEquals(0f, mShareTarget5.getScore(), DELTA); + assertEquals(0f, mShareTarget6.getScore(), DELTA); + } + + private static UsageEvents.Event createUsageEvent(String packageName) { + UsageEvents.Event e = new UsageEvents.Event(); + e.mPackage = packageName; + return e; + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 40ada2aedd59..db1bbab7ed94 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -43,6 +43,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; +import com.android.server.pm.parsing.PackageParser2; import org.junit.Before; import org.junit.Test; @@ -63,6 +64,7 @@ public class ApexManagerTest { private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; private Context mContext; + private PackageParser2 mPackageParser2; private IApexService mApexService = mock(IApexService.class); @@ -70,11 +72,14 @@ public class ApexManagerTest { public void setUp() throws RemoteException { mContext = InstrumentationRegistry.getInstrumentation().getContext(); mApexManager = new ApexManager.ApexManagerImpl(mApexService); + mPackageParser2 = new PackageParser2(null, false, null, null, null); } @Test public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); @@ -90,6 +95,8 @@ public class ApexManagerTest { @Test public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); @@ -105,6 +112,8 @@ public class ApexManagerTest { @Test public void testGetPackageInfo_setFlagsNone() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull(); } @@ -112,6 +121,8 @@ public class ApexManagerTest { @Test public void testGetActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getActivePackages()).isNotEmpty(); } @@ -119,6 +130,8 @@ public class ApexManagerTest { @Test public void testGetActivePackages_noneActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getActivePackages()).isEmpty(); } @@ -126,6 +139,8 @@ public class ApexManagerTest { @Test public void testGetFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getFactoryPackages()).isNotEmpty(); } @@ -133,6 +148,8 @@ public class ApexManagerTest { @Test public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getFactoryPackages()).isEmpty(); } @@ -140,6 +157,8 @@ public class ApexManagerTest { @Test public void testGetInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getInactivePackages()).isNotEmpty(); } @@ -147,6 +166,8 @@ public class ApexManagerTest { @Test public void testGetInactivePackages_noneInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getInactivePackages()).isEmpty(); } @@ -154,6 +175,8 @@ public class ApexManagerTest { @Test public void testIsApexPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 155c6ddd4507..fcbd5072ae35 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -34,6 +34,7 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -54,6 +55,7 @@ import java.util.Map; * Tests for {@link TunerResourceManagerService} class. */ @SmallTest +@Presubmit @RunWith(JUnit4.class) public class TunerResourceManagerServiceTest { private static final String TAG = "TunerResourceManagerServiceTest"; diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java index ab5665ba99fc..2ff178eba3e1 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java @@ -18,6 +18,7 @@ package com.android.server.tv.tunerresourcemanager; import static com.google.common.truth.Truth.assertThat; import android.media.tv.TvInputService; +import android.platform.test.annotations.Presubmit; import android.util.Slog; import androidx.test.filters.SmallTest; @@ -36,6 +37,7 @@ import java.nio.charset.StandardCharsets; * Tests for {@link UseCasePriorityHints} class. */ @SmallTest +@Presubmit @RunWith(JUnit4.class) public class UseCasePriorityHintsTest { private static final String TAG = "UseCasePriorityHintsTest"; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 52fc3de89451..12934ee8bb78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -27,6 +27,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -56,7 +57,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.IApplicationThread; @@ -1157,6 +1157,34 @@ public class ActivityStackTests extends ActivityTestsBase { } @Test + public void testCheckBehindFullscreenActivity() { + final ActivityRecord bottomActivity = + new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + final ActivityRecord topActivity = + new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + doReturn(true).when(mStack).shouldBeVisible(any()); + assertTrue(mStack.checkBehindFullscreenActivity(bottomActivity, + null /* handleBehindFullscreenActivity */)); + assertFalse(mStack.checkBehindFullscreenActivity(topActivity, + null /* handleBehindFullscreenActivity */)); + + doReturn(false).when(topActivity).occludesParent(); + assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity, + null /* handleBehindFullscreenActivity */)); + assertFalse(mStack.checkBehindFullscreenActivity(topActivity, + null /* handleBehindFullscreenActivity */)); + + final ActivityRecord finishingActivity = + new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build(); + finishingActivity.finishing = true; + doCallRealMethod().when(finishingActivity).occludesParent(); + assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity, + null /* handleBehindFullscreenActivity */)); + assertFalse(mStack.checkBehindFullscreenActivity(topActivity, + null /* handleBehindFullscreenActivity */)); + } + + @Test public void testClearUnknownAppVisibilityBehindFullscreenActivity() { final UnknownAppVisibilityController unknownAppVisibilityController = mDefaultDisplay.mDisplayContent.mUnknownAppVisibilityController; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index c71d8194f889..08e492a7b0ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -38,7 +37,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -70,7 +68,7 @@ public class AppTransitionTests extends WindowTestsBase { @Before public void setUp() throws Exception { - doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + doNothing().when(mWm.mRoot).performSurfacePlacement(); mDc = mWm.getDefaultDisplayContentLocked(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 6cc57f4dbf8c..cf3cfecbf65e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -174,7 +174,7 @@ public class AppWindowTokenTests extends WindowTestsBase { null /* freezeThisOneIfNeeded */, false /* forceUpdate */); // In this test, DC will not get config update. Set the waiting flag to false. mDisplayContent.mWaitingForConfig = false; - mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); + mWm.mRoot.performSurfacePlacement(); assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); assertTrue(appWindow.mResizeReported); appWindow.removeImmediately(); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 66566bc5dff5..da3ee3990137 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -28,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.TYPE_VIRTUAL; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -54,6 +56,7 @@ import static java.lang.Integer.MAX_VALUE; import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.WindowConfiguration; import android.content.ComponentName; @@ -681,24 +684,19 @@ public class RecentTasksTest extends ActivityTestsBase { * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed. */ @Test - public void testVisibleTasks_singleTaskDisplay() { + public void testVisibleTasks_alwaysOnTop() { mRecentTasks.setOnlyTestVisibleRange(); mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */); - final DisplayContent singleTaskDisplay = - addNewDisplayContentAt(DisplayContent.POSITION_TOP); - singleTaskDisplay.setDisplayToSingleTaskInstance(); - ActivityStack singleTaskStack = singleTaskDisplay.createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final Task alwaysOnTopTask = display.createStack(WINDOWING_MODE_MULTI_WINDOW, + ACTIVITY_TYPE_STANDARD, true /* onTop */); + alwaysOnTopTask.setAlwaysOnTop(true); - Task excludedTask1 = createTaskBuilder(".ExcludedTask1") - .setStack(singleTaskStack) - .build(); + assertFalse("Always on top tasks should not be visible recents", + mRecentTasks.isVisibleRecentTask(alwaysOnTopTask)); - assertFalse("Tasks on singleTaskDisplay should not be visible recents", - mRecentTasks.isVisibleRecentTask(excludedTask1)); - - mRecentTasks.add(excludedTask1); + mRecentTasks.add(alwaysOnTopTask); // Add N+1 visible tasks. mRecentTasks.add(mTasks.get(0)); @@ -1366,12 +1364,12 @@ public class RecentTasksTest extends ActivityTestsBase { public boolean mLastAllowed; @Override - void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType, - int ignoreWindowingMode, RootWindowContainer root, - int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) { + void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents, + RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser, + ArraySet<Integer> profileIds) { mLastAllowed = allowed; - super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, root, - callingUid, allowed, crossUser, profileIds); + super.getTasks(maxNum, list, filterOnlyVisibleRecents, root, callingUid, allowed, + crossUser, profileIds); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 406affc27b0a..da07baca3ce1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -100,7 +100,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + doNothing().when(mWm.mRoot).performSurfacePlacement(); when(mMockRunner.asBinder()).thenReturn(new Binder()); mDefaultDisplay = mWm.mRoot.getDefaultDisplay(); mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, @@ -403,11 +403,11 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { wallpapers.get(0).getConfiguration().orientation); // Wallpaper's transform state is controlled by home, so the invocation should be no-op. - wallpaperWindowToken.clearFixedRotationTransform(); + wallpaperWindowToken.finishFixedRotationTransform(); assertTrue(wallpaperWindowToken.hasFixedRotationTransform()); // Wallpaper's transform state should be cleared with home. - homeActivity.clearFixedRotationTransform(); + homeActivity.finishFixedRotationTransform(); assertFalse(wallpaperWindowToken.hasFixedRotationTransform()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 0ef25824df2a..e841e434ea82 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -788,6 +788,22 @@ public class RootActivityContainerTests extends ActivityTestsBase { } @Test + public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() { + // Create a root task with an activity on secondary display. + final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mService, 300, + 600).build(); + final Task task = new ActivityTestsBase.StackBuilder(mRootWindowContainer).setDisplay( + secondaryDisplay).build(); + final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mService) + .setTask(task).build(); + + // Make sure the root task is valid and can be reused on default display. + final ActivityStack stack = mRootWindowContainer.getValidLaunchStackOnDisplay( + DEFAULT_DISPLAY, activity, task, null, null); + assertEquals(task, stack); + } + + @Test public void testSwitchUser_missingHomeRootTask() { doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack(); diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 0d5565428bf2..d6a67abc9e76 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -16,9 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -82,9 +79,8 @@ public class RunningTasksTest extends ActivityTestsBase { // collected from all tasks across all the stacks final int numFetchTasks = 5; ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); - mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED, - mRootWindowContainer, -1 /* callingUid */, true /* allowed */, - true /*crossUser */, PROFILE_IDS); + mRunningTasks.getTasks(5, tasks, false /* filterOnlyVisibleRecents */, mRootWindowContainer, + -1 /* callingUid */, true /* allowed */, true /*crossUser */, PROFILE_IDS); assertThat(tasks).hasSize(numFetchTasks); for (int i = 0; i < numFetchTasks; i++) { assertEquals(numTasks - i - 1, tasks.get(i).id); @@ -93,9 +89,9 @@ public class RunningTasksTest extends ActivityTestsBase { // Ensure that requesting more than the total number of tasks only returns the subset // and does not crash tasks.clear(); - mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED, - mRootWindowContainer, -1 /* callingUid */, true /* allowed */, - true /* crossUser */, PROFILE_IDS); + mRunningTasks.getTasks(100, tasks, false /* filterOnlyVisibleRecents */, + mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /* crossUser */, + PROFILE_IDS); assertThat(tasks).hasSize(numTasks); for (int i = 0; i < numTasks; i++) { assertEquals(numTasks - i - 1, tasks.get(i).id); @@ -119,9 +115,9 @@ public class RunningTasksTest extends ActivityTestsBase { final int numFetchTasks = 5; final ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); - mRunningTasks.getTasks(numFetchTasks, tasks, ACTIVITY_TYPE_UNDEFINED, - WINDOWING_MODE_UNDEFINED, mRootWindowContainer, -1 /* callingUid */, - true /* allowed */, true /*crossUser */, PROFILE_IDS); + mRunningTasks.getTasks(numFetchTasks, tasks, false /* filterOnlyVisibleRecents */, + mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /*crossUser */, + PROFILE_IDS); assertThat(tasks).hasSize(numFetchTasks); for (int i = 0; i < tasks.size(); i++) { final Bundle extras = tasks.get(i).baseIntent.getExtras(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index edf81ea32ad3..893a14541c48 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -481,7 +481,7 @@ public class SizeCompatTests extends ActivityTestsBase { // The letterbox needs a main window to layout. addWindowToActivity(mActivity); // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}. - mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */); + mActivity.mRootWindowContainer.performSurfacePlacement(); // The letterbox insets should be [350, 0 - 350, 0]. assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0), mActivity.getLetterboxInsets()); diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 0dca006f37c0..ffd25c08a8ba 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -2633,6 +2633,7 @@ public abstract class ConnectionService extends Service { * @param request Details about the incoming call. * @return The {@code Connection} object to satisfy this call, or {@code null} to * not handle the call. + * @hide */ public @Nullable Conference onCreateIncomingConference( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, @@ -2717,6 +2718,7 @@ public abstract class ConnectionService extends Service { * @param connectionManagerPhoneAccount See description at * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. * @param request The incoming connection request. + * @hide */ public void onCreateIncomingConferenceFailed( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, @@ -2737,6 +2739,7 @@ public abstract class ConnectionService extends Service { * @param connectionManagerPhoneAccount See description at * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}. * @param request The outgoing connection request. + * @hide */ public void onCreateOutgoingConferenceFailed( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, @@ -2805,6 +2808,7 @@ public abstract class ConnectionService extends Service { * @param request Details about the outgoing call. * @return The {@code Conference} object to satisfy this call, or the result of an invocation * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call. + * @hide */ public @Nullable Conference onCreateOutgoingConference( @Nullable PhoneAccountHandle connectionManagerPhoneAccount, diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 0b331744d922..7e02966779a2 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -442,16 +442,40 @@ public final class TelephonyPermissions { // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they // will be denied access, even if they have another permission and AppOps bit if needed. - // First, check if we can read the phone state and the SDK version is below R. + // First, check if the SDK version is below R + boolean preR = false; try { ApplicationInfo info = context.getPackageManager().getApplicationInfoAsUser( callingPackage, 0, UserHandle.getUserHandleForUid(Binder.getCallingUid())); - if (info.targetSdkVersion <= Build.VERSION_CODES.Q) { + preR = info.targetSdkVersion <= Build.VERSION_CODES.Q; + } catch (PackageManager.NameNotFoundException nameNotFoundException) { + } + if (preR) { + // SDK < R allows READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier privilege + try { return checkReadPhoneState( context, subId, pid, uid, callingPackage, callingFeatureId, message); + } catch (SecurityException readPhoneStateException) { + } + } else { + // SDK >= R allows READ_PRIVILEGED_PHONE_STATE or carrier privilege + try { + context.enforcePermission( + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message); + // Skip checking for runtime permission since caller has privileged permission + return true; + } catch (SecurityException readPrivilegedPhoneStateException) { + if (SubscriptionManager.isValidSubscriptionId(subId)) { + try { + enforceCarrierPrivilege(context, subId, uid, message); + // Skip checking for runtime permission since caller has carrier privilege + return true; + } catch (SecurityException carrierPrivilegeException) { + } + } } - } catch (SecurityException | PackageManager.NameNotFoundException e) { } + // Can be read with READ_SMS too. try { context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message); diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index 558f4cd24471..39a754389254 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -19,9 +19,9 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.hardware.radio.V1_1.EutranBands; import android.hardware.radio.V1_1.GeranBands; import android.hardware.radio.V1_5.AccessNetwork; +import android.hardware.radio.V1_5.EutranBands; import android.hardware.radio.V1_5.UtranBands; import java.lang.annotation.Retention; @@ -212,7 +212,8 @@ public final class AccessNetworkConstants { /** * Frequency bands for EUTRAN. - * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf + * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands + * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf */ public static final class EutranBand { public static final int BAND_1 = EutranBands.BAND_1; @@ -259,10 +260,22 @@ public final class AccessNetworkConstants { public static final int BAND_46 = EutranBands.BAND_46; public static final int BAND_47 = EutranBands.BAND_47; public static final int BAND_48 = EutranBands.BAND_48; + public static final int BAND_49 = EutranBands.BAND_49; + public static final int BAND_50 = EutranBands.BAND_50; + public static final int BAND_51 = EutranBands.BAND_51; + public static final int BAND_52 = EutranBands.BAND_52; + public static final int BAND_53 = EutranBands.BAND_53; public static final int BAND_65 = EutranBands.BAND_65; public static final int BAND_66 = EutranBands.BAND_66; public static final int BAND_68 = EutranBands.BAND_68; public static final int BAND_70 = EutranBands.BAND_70; + public static final int BAND_71 = EutranBands.BAND_71; + public static final int BAND_72 = EutranBands.BAND_72; + public static final int BAND_73 = EutranBands.BAND_73; + public static final int BAND_74 = EutranBands.BAND_74; + public static final int BAND_85 = EutranBands.BAND_85; + public static final int BAND_87 = EutranBands.BAND_87; + public static final int BAND_88 = EutranBands.BAND_88; /** @hide */ private EutranBand() {}; @@ -305,9 +318,11 @@ public final class AccessNetworkConstants { /** * Frequency bands for NGRAN + * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810101/15.08.02_60/ts_13810101v150802p.pdf + * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf */ public static final class NgranBands { - /** FR1 bands */ + /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */ public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1; public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2; public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3; @@ -346,9 +361,15 @@ public final class AccessNetworkConstants { public static final int BAND_83 = android.hardware.radio.V1_5.NgranBands.BAND_83; public static final int BAND_84 = android.hardware.radio.V1_5.NgranBands.BAND_84; public static final int BAND_86 = android.hardware.radio.V1_5.NgranBands.BAND_86; + public static final int BAND_89 = android.hardware.radio.V1_5.NgranBands.BAND_89; public static final int BAND_90 = android.hardware.radio.V1_5.NgranBands.BAND_90; + public static final int BAND_91 = android.hardware.radio.V1_5.NgranBands.BAND_91; + public static final int BAND_92 = android.hardware.radio.V1_5.NgranBands.BAND_92; + public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93; + public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94; + public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95; - /** FR2 bands */ + /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */ public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257; public static final int BAND_258 = android.hardware.radio.V1_5.NgranBands.BAND_258; public static final int BAND_260 = android.hardware.radio.V1_5.NgranBands.BAND_260; @@ -398,7 +419,13 @@ public final class AccessNetworkConstants { BAND_83, BAND_84, BAND_86, + BAND_89, BAND_90, + BAND_91, + BAND_92, + BAND_93, + BAND_94, + BAND_95, BAND_257, BAND_258, BAND_260, @@ -495,7 +522,13 @@ public final class AccessNetworkConstants { case BAND_83: case BAND_84: case BAND_86: + case BAND_89: case BAND_90: + case BAND_91: + case BAND_92: + case BAND_93: + case BAND_94: + case BAND_95: return FREQUENCY_RANGE_GROUP_1; case BAND_257: case BAND_258: diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java index 5d2c225f28ec..981ed450004a 100644 --- a/telephony/java/android/telephony/AccessNetworkUtils.java +++ b/telephony/java/android/telephony/AccessNetworkUtils.java @@ -34,12 +34,10 @@ public class AccessNetworkUtils { return DUPLEX_MODE_UNKNOWN; } - if (band >= EutranBand.BAND_68) { + if (band > EutranBand.BAND_88) { return DUPLEX_MODE_UNKNOWN; } else if (band >= EutranBand.BAND_65) { return DUPLEX_MODE_FDD; - } else if (band >= EutranBand.BAND_47) { - return DUPLEX_MODE_UNKNOWN; } else if (band >= EutranBand.BAND_33) { return DUPLEX_MODE_TDD; } else if (band >= EutranBand.BAND_1) { @@ -58,17 +56,53 @@ public class AccessNetworkUtils { * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists */ public static int getOperatingBandForEarfcn(int earfcn) { - if (earfcn > 67535) { + if (earfcn > 70645) { + return INVALID_BAND; + } else if (earfcn >= 70596) { + return EutranBand.BAND_88; + } else if (earfcn >= 70546) { + return EutranBand.BAND_87; + } else if (earfcn >= 70366) { + return EutranBand.BAND_85; + } else if (earfcn > 69465) { + return INVALID_BAND; + } else if (earfcn >= 69036) { + return EutranBand.BAND_74; + } else if (earfcn >= 68986) { + return EutranBand.BAND_73; + } else if (earfcn >= 68936) { + return EutranBand.BAND_72; + } else if (earfcn >= 68586) { + return EutranBand.BAND_71; + } else if (earfcn >= 68336) { + return EutranBand.BAND_70; + } else if (earfcn > 67835) { return INVALID_BAND; + } else if (earfcn >= 67536) { + return EutranBand.BAND_68; } else if (earfcn >= 67366) { return INVALID_BAND; // band 67 only for CarrierAgg } else if (earfcn >= 66436) { return EutranBand.BAND_66; } else if (earfcn >= 65536) { return EutranBand.BAND_65; - } else if (earfcn > 54339) { + } else if (earfcn > 60254) { return INVALID_BAND; - } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) { + } else if (earfcn >= 60140) { + return EutranBand.BAND_53; + } else if (earfcn >= 59140) { + return EutranBand.BAND_52; + } else if (earfcn >= 59090) { + return EutranBand.BAND_51; + } else if (earfcn >= 58240) { + return EutranBand.BAND_50; + } else if (earfcn >= 56740) { + return EutranBand.BAND_49; + } else if (earfcn >= 55240) { + return EutranBand.BAND_48; + } else if (earfcn >= 54540) { + return EutranBand.BAND_47; + } else if (earfcn >= 46790) { return EutranBand.BAND_46; } else if (earfcn >= 46590) { return EutranBand.BAND_45; diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java index 1c92705b9422..d00049c1ebe5 100644 --- a/telephony/java/android/telephony/CellSignalStrengthCdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java @@ -314,6 +314,8 @@ public final class CellSignalStrengthCdma extends CellSignalStrength implements /** * Get the signal strength as dBm + * + * @return min(CDMA RSSI, EVDO RSSI) of the measured cell. */ @Override public int getDbm() { diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 76d2df918423..9d55f109f751 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -145,6 +145,8 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P /** * Get the signal strength as dBm. + * + * @return the RSSI of the measured cell. */ @Override public int getDbm() { diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index c1447465f53f..991375c5bc73 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2520,7 +2520,6 @@ public final class SmsManager { * @param sentIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is successfully sent, or failed * @throws IllegalArgumentException if contentUri is empty - * @deprecated use {@link MmsManager#sendMultimediaMessage} instead. */ public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) { @@ -2555,7 +2554,6 @@ public final class SmsManager { * @param downloadedIntent if not NULL this <code>PendingIntent</code> is * broadcast when the message is downloaded, or the download is failed * @throws IllegalArgumentException if locationUrl or contentUri is empty - * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead. */ public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) { diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 5d3cc44ac68c..2facd5a16bc8 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -944,6 +944,18 @@ public class SubscriptionManager { if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN"); } + /** + * Callback invoked when {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * Executor, OnSubscriptionsChangedListener)} or + * {@link SubscriptionManager#addOnSubscriptionsChangedListener( + * OnSubscriptionsChangedListener)} fails to complete due to the + * {@link Context#TELEPHONY_REGISTRY_SERVICE} being unavailable. + * @hide + */ + public void onAddListenerFailed() { + Rlog.w(LOG_TAG, "onAddListenerFailed not overridden"); + } + private void log(String s) { Rlog.d(LOG_TAG, s); } @@ -1012,6 +1024,12 @@ public class SubscriptionManager { if (telephonyRegistryManager != null) { telephonyRegistryManager.addOnSubscriptionsChangedListener(listener, executor); + } else { + // If the telephony registry isn't available, we will inform the caller on their + // listener that it failed so they can try to re-register. + loge("addOnSubscriptionsChangedListener: pkgname=" + pkgName + " failed to be added " + + " due to TELEPHONY_REGISTRY_SERVICE being unavailable."); + executor.execute(() -> listener.onAddListenerFailed()); } } diff --git a/test-runner/src/android/test/TouchUtils.java b/test-runner/src/android/test/TouchUtils.java index bb4e00ba1ef9..f2f0be73c010 100644 --- a/test-runner/src/android/test/TouchUtils.java +++ b/test-runner/src/android/test/TouchUtils.java @@ -223,7 +223,7 @@ public class TouchUtils { public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v, int stepCount) { int screenHeight = - activity.getWindowManager().getCurrentWindowMetrics().getSize().getHeight(); + activity.getWindowManager().getCurrentWindowMetrics().getBounds().height(); int[] xy = new int[2]; v.getLocationOnScreen(xy); diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 9be97b505a3f..0ad30391d1a8 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -46,6 +46,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -72,6 +73,7 @@ public class AppLaunch extends InstrumentationTestCase { private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts"; private static final String KEY_APPS = "apps"; private static final String KEY_IORAP_TRIAL_LAUNCH = "iorap_trial_launch"; + private static final String KEY_IORAP_COMPILER_FILTERS = "iorap_compiler_filters"; private static final String KEY_TRIAL_LAUNCH = "trial_launch"; private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations"; private static final String KEY_LAUNCH_ORDER = "launch_order"; @@ -153,6 +155,7 @@ public class AppLaunch extends InstrumentationTestCase { private BufferedWriter mBufferedWriter = null; private boolean mSimplePerfAppOnly = false; private String[] mCompilerFilters = null; + private List<String> mIorapCompilerFilters = null; private String mLastAppName = ""; private boolean mCycleCleanUp = false; private boolean mTraceAll = false; @@ -618,6 +621,24 @@ public class AppLaunch extends InstrumentationTestCase { return reason; } + private boolean shouldIncludeIorap(String compilerFilter) { + if (!mIorapTrialLaunch) { + return false; + } + + // No iorap compiler filters specified: treat all compiler filters as ok. + if (mIorapCompilerFilters == null) { + return true; + } + + // iorap compiler filters specified: the compilerFilter must be in the whitelist. + if (mIorapCompilerFilters.indexOf(compilerFilter) != -1) { + return true; + } + + return false; + } + /** * If launch order is "cyclic" then apps will be launched one after the * other for each iteration count. @@ -632,7 +653,7 @@ public class AppLaunch extends InstrumentationTestCase { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false)); } } - if (mIorapTrialLaunch) { + if (shouldIncludeIorap(compilerFilter)) { for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) { for (String app : mNameToResultKey.keySet()) { String reason = makeReasonForIorapTrialLaunch(launchCount); @@ -646,14 +667,16 @@ public class AppLaunch extends InstrumentationTestCase { for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { for (String app : mNameToResultKey.keySet()) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(LAUNCH_ITERATION, launchCount), mIorapTrialLaunch)); + String.format(LAUNCH_ITERATION, launchCount), + shouldIncludeIorap(compilerFilter))); } } if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { for (String app : mNameToResultKey.keySet()) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(TRACE_ITERATION, traceCount), mIorapTrialLaunch)); + String.format(TRACE_ITERATION, traceCount), + shouldIncludeIorap(compilerFilter))); } } } @@ -664,7 +687,7 @@ public class AppLaunch extends InstrumentationTestCase { if (mTrialLaunch) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false)); } - if (mIorapTrialLaunch) { + if (shouldIncludeIorap(compilerFilter)) { for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) { String reason = makeReasonForIorapTrialLaunch(launchCount); mLaunchOrderList.add( @@ -675,12 +698,14 @@ public class AppLaunch extends InstrumentationTestCase { } for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(LAUNCH_ITERATION, launchCount), mIorapTrialLaunch)); + String.format(LAUNCH_ITERATION, launchCount), + shouldIncludeIorap(compilerFilter))); } if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) { for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) { mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, - String.format(TRACE_ITERATION, traceCount), mIorapTrialLaunch)); + String.format(TRACE_ITERATION, traceCount), + shouldIncludeIorap(compilerFilter))); } } } @@ -767,7 +792,7 @@ public class AppLaunch extends InstrumentationTestCase { .executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable)); getInstrumentation().getUiAutomation() .executeShellCommand("start iorapd"); - sleep(2000); // give enough time for iorapd to start back up. + sleep(3000); // give enough time for iorapd to start back up. if (enable) { mIorapStatus = IorapStatus.ENABLED; @@ -822,6 +847,13 @@ public class AppLaunch extends InstrumentationTestCase { mCompilerFilters = new String[1]; } + String iorapCompilerFilterList = args.getString(KEY_IORAP_COMPILER_FILTERS); + if (iorapCompilerFilterList != null) { + // Passing in iorap compiler filters implies an iorap trial launch. + mIorapTrialLaunch = true; + mIorapCompilerFilters = Arrays.asList(iorapCompilerFilterList.split("\\|")); + } + // Pre-populate the results map to avoid null checks. for (String app : mNameToLaunchTime.keySet()) { HashMap<String, List<AppLaunchResult>> map = new HashMap<>(); diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java index 42908bef6178..35a6c26ec73f 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java @@ -167,7 +167,7 @@ public class DummyBlobData { final byte[] actualBytes = new byte[lengthBytes]; try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream( - session.openWrite(0L, 0L))) { + session.openRead())) { read(in, actualBytes, offsetBytes, lengthBytes); } @@ -190,7 +190,7 @@ public class DummyBlobData { long offsetBytes, long lengthBytes) throws Exception { final byte[] actualDigest; try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream( - session.openWrite(0L, 0L))) { + session.openRead())) { actualDigest = createSha256Digest(in, offsetBytes, lengthBytes); } diff --git a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java index e255ce234c65..31532a226800 100644 --- a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java +++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java @@ -27,7 +27,6 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; -import android.util.Size; import android.view.Gravity; import android.view.IWindowManager; import android.view.MotionEvent; @@ -90,8 +89,8 @@ public class MirrorSurfaceActivity extends Activity implements View.OnClickListe .getSystemService(WindowManager.class); mIWm = WindowManagerGlobal.getWindowManagerService(); - Size windowSize = mWm.getCurrentWindowMetrics().getSize(); - mWindowBounds.set(0, 0, windowSize.getWidth(), windowSize.getHeight()); + Rect windowBounds = mWm.getCurrentWindowMetrics().getBounds(); + mWindowBounds.set(0, 0, windowBounds.width(), windowBounds.height()); mScaleText = findViewById(R.id.scale); mDisplayFrameText = findViewById(R.id.displayFrame); diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index a269e17a8cff..457e0db9dc54 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -516,9 +516,6 @@ public final class SoftApConfiguration implements Parcelable { public WifiConfiguration toWifiConfiguration() { WifiConfiguration wifiConfig = new WifiConfiguration(); wifiConfig.SSID = mSsid; - if (mBssid != null) { - wifiConfig.BSSID = mBssid.toString(); - } wifiConfig.preSharedKey = mPassphrase; wifiConfig.hiddenSSID = mHiddenSsid; wifiConfig.apChannel = mChannel; @@ -662,8 +659,6 @@ public final class SoftApConfiguration implements Parcelable { /** * Specifies a BSSID for the AP. * <p> - * Only supported when configuring a local-only hotspot. - * <p> * <li>If not set, defaults to null.</li> * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is * responsible for avoiding collisions. |