diff options
409 files changed, 7035 insertions, 2667 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 2457e707ef57..372ae6da4959 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -53,6 +53,7 @@ aconfig_srcjars = [ ":android.credentials.flags-aconfig-java{.generated_srcjars}", ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}", ":android.service.voice.flags-aconfig-java{.generated_srcjars}", + ":android.media.tv.flags-aconfig-java{.generated_srcjars}", ":aconfig_midi_flags_java_lib{.generated_srcjars}", ":android.service.autofill.flags-aconfig-java{.generated_srcjars}", ":com.android.net.flags-aconfig-java{.generated_srcjars}", @@ -379,6 +380,19 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// Media TV +aconfig_declarations { + name: "android.media.tv.flags-aconfig", + package: "android.media.tv.flags", + srcs: ["media/java/android/media/tv/flags/media_tv.aconfig"], +} + +java_aconfig_library { + name: "android.media.tv.flags-aconfig-java", + aconfig_declarations: "android.media.tv.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // Media Audio java_aconfig_library { name: "com.android.media.audio.flags-aconfig-java", diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 1287cb4bb952..23b36e20b174 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -1820,7 +1820,11 @@ public class JobSchedulerService extends com.android.server.SystemService jobStatus.getEstimatedNetworkUploadBytes(), jobStatus.getWorkCount(), ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())), - jobStatus.getNamespaceHash()); + jobStatus.getNamespaceHash(), + /* system_measured_source_download_bytes */0, + /* system_measured_source_upload_bytes */ 0, + /* system_measured_calling_download_bytes */0, + /* system_measured_calling_upload_bytes */ 0); // If the job is immediately ready to run, then we can just immediately // put it in the pending list and try to schedule it. This is especially @@ -2257,7 +2261,11 @@ public class JobSchedulerService extends com.android.server.SystemService cancelled.getEstimatedNetworkUploadBytes(), cancelled.getWorkCount(), ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())), - cancelled.getNamespaceHash()); + cancelled.getNamespaceHash(), + /* system_measured_source_download_bytes */ 0, + /* system_measured_source_upload_bytes */ 0, + /* system_measured_calling_download_bytes */0, + /* system_measured_calling_upload_bytes */ 0); } // If this is a replacement, bring in the new version of the job if (incomingJob != null) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 79653f0e0a91..2d49cfb3b171 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -45,6 +45,7 @@ import android.content.Intent; import android.content.PermissionChecker; import android.content.ServiceConnection; import android.net.Network; +import android.net.TrafficStats; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -241,6 +242,14 @@ public final class JobServiceContext implements ServiceConnection { private int mDeathMarkInternalStopReason; private String mDeathMarkDebugReason; + private long mInitialDownloadedBytesFromSource; + + private long mInitialUploadedBytesFromSource; + + private long mInitialDownloadedBytesFromCalling; + + private long mInitialUploadedBytesFromCalling; + // Debugging: reason this job was last stopped. public String mStoppedReason; @@ -472,6 +481,14 @@ public final class JobServiceContext implements ServiceConnection { } mJobPackageTracker.noteActive(job); final int sourceUid = job.getSourceUid(); + + // Measure UID baseline traffic for deltas + mInitialDownloadedBytesFromSource = TrafficStats.getUidRxBytes(sourceUid); + mInitialUploadedBytesFromSource = TrafficStats.getUidTxBytes(sourceUid); + + mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid()); + mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid()); + FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid}, // Given that the source tag is set by the calling app, it should be connected @@ -517,7 +534,11 @@ public final class JobServiceContext implements ServiceConnection { job.getEstimatedNetworkUploadBytes(), job.getWorkCount(), ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())), - job.getNamespaceHash()); + job.getNamespaceHash(), + /* system_measured_source_download_bytes */ 0, + /* system_measured_source_upload_bytes */ 0, + /* system_measured_calling_download_bytes */ 0, + /* system_measured_calling_upload_bytes */ 0); sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount()); final String sourcePackage = job.getSourcePackageName(); if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { @@ -1586,7 +1607,15 @@ public final class JobServiceContext implements ServiceConnection { completedJob.getWorkCount(), ActivityManager .processStateAmToProto(mService.getUidProcState(completedJob.getUid())), - completedJob.getNamespaceHash()); + completedJob.getNamespaceHash(), + TrafficStats.getUidRxBytes(completedJob.getSourceUid()) + - mInitialDownloadedBytesFromSource, + TrafficStats.getUidTxBytes(completedJob.getSourceUid()) + - mInitialUploadedBytesFromSource, + TrafficStats.getUidRxBytes(completedJob.getUid()) + - mInitialDownloadedBytesFromCalling, + TrafficStats.getUidTxBytes(completedJob.getUid()) + - mInitialUploadedBytesFromCalling); if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", getId()); diff --git a/core/api/current.txt b/core/api/current.txt index 805dfc679d4f..7d5157457cd2 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -11989,22 +11989,22 @@ package android.content.pm { method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo); } - @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivity { - ctor public ArchivedActivity(@NonNull CharSequence, @NonNull android.content.ComponentName); + @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivityInfo { + ctor public ArchivedActivityInfo(@NonNull CharSequence, @NonNull android.content.ComponentName); method @NonNull public android.content.ComponentName getComponentName(); method @Nullable public android.graphics.drawable.Drawable getIcon(); method @NonNull public CharSequence getLabel(); method @Nullable public android.graphics.drawable.Drawable getMonochromeIcon(); - method @NonNull public android.content.pm.ArchivedActivity setComponentName(@NonNull android.content.ComponentName); - method @NonNull public android.content.pm.ArchivedActivity setIcon(@NonNull android.graphics.drawable.Drawable); - method @NonNull public android.content.pm.ArchivedActivity setLabel(@NonNull CharSequence); - method @NonNull public android.content.pm.ArchivedActivity setMonochromeIcon(@NonNull android.graphics.drawable.Drawable); + method @NonNull public android.content.pm.ArchivedActivityInfo setComponentName(@NonNull android.content.ComponentName); + method @NonNull public android.content.pm.ArchivedActivityInfo setIcon(@NonNull android.graphics.drawable.Drawable); + method @NonNull public android.content.pm.ArchivedActivityInfo setLabel(@NonNull CharSequence); + method @NonNull public android.content.pm.ArchivedActivityInfo setMonochromeIcon(@NonNull android.graphics.drawable.Drawable); } - @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackage { - ctor public ArchivedPackage(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivity>); + @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackageInfo { + ctor public ArchivedPackageInfo(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivityInfo>); method @Nullable public String getDefaultToDeviceProtectedStorage(); - method @NonNull public java.util.List<android.content.pm.ArchivedActivity> getLauncherActivities(); + method @NonNull public java.util.List<android.content.pm.ArchivedActivityInfo> getLauncherActivities(); method @NonNull public String getPackageName(); method @Nullable public String getRequestLegacyExternalStorage(); method @NonNull public android.content.pm.SigningInfo getSigningInfo(); @@ -12012,15 +12012,15 @@ package android.content.pm { method @Nullable public String getUserDataFragile(); method public int getVersionCode(); method public int getVersionCodeMajor(); - method @NonNull public android.content.pm.ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String); - method @NonNull public android.content.pm.ArchivedPackage setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivity>); - method @NonNull public android.content.pm.ArchivedPackage setPackageName(@NonNull String); - method @NonNull public android.content.pm.ArchivedPackage setRequestLegacyExternalStorage(@NonNull String); - method @NonNull public android.content.pm.ArchivedPackage setSigningInfo(@NonNull android.content.pm.SigningInfo); - method @NonNull public android.content.pm.ArchivedPackage setTargetSdkVersion(int); - method @NonNull public android.content.pm.ArchivedPackage setUserDataFragile(@NonNull String); - method @NonNull public android.content.pm.ArchivedPackage setVersionCode(int); - method @NonNull public android.content.pm.ArchivedPackage setVersionCodeMajor(int); + method @NonNull public android.content.pm.ArchivedPackageInfo setDefaultToDeviceProtectedStorage(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackageInfo setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivityInfo>); + method @NonNull public android.content.pm.ArchivedPackageInfo setPackageName(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackageInfo setRequestLegacyExternalStorage(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackageInfo setSigningInfo(@NonNull android.content.pm.SigningInfo); + method @NonNull public android.content.pm.ArchivedPackageInfo setTargetSdkVersion(int); + method @NonNull public android.content.pm.ArchivedPackageInfo setUserDataFragile(@NonNull String); + method @NonNull public android.content.pm.ArchivedPackageInfo setVersionCode(int); + method @NonNull public android.content.pm.ArchivedPackageInfo setVersionCodeMajor(int); } public final class Attribution implements android.os.Parcelable { @@ -12355,7 +12355,7 @@ package android.content.pm { method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int); method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions(); method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender); - method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackage, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender); + method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackageInfo, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender); method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException; method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback); method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler); @@ -12637,7 +12637,7 @@ package android.content.pm { method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo); method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; - method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackage getArchivedPackage(@NonNull String); + method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackageInfo getArchivedPackage(@NonNull String); method @NonNull public CharSequence getBackgroundPermissionOptionLabel(); method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int); method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName); @@ -23842,14 +23842,14 @@ package android.media { field @FlaggedApi("com.android.media.flags.enable_audio_policies_device_and_bluetooth_controller") public static final int TYPE_HDMI_EARC = 29; // 0x1d field public static final int TYPE_HEARING_AID = 23; // 0x17 field public static final int TYPE_REMOTE_AUDIO_VIDEO_RECEIVER = 1003; // 0x3eb - field public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0 - field public static final int TYPE_REMOTE_COMPUTER = 1006; // 0x3ee - field public static final int TYPE_REMOTE_GAME_CONSOLE = 1007; // 0x3ef - field public static final int TYPE_REMOTE_SMARTPHONE = 1010; // 0x3f2 - field public static final int TYPE_REMOTE_SMARTWATCH = 1009; // 0x3f1 + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_CAR = 1008; // 0x3f0 + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_COMPUTER = 1006; // 0x3ee + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_GAME_CONSOLE = 1007; // 0x3ef + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_SMARTPHONE = 1010; // 0x3f2 + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_SMARTWATCH = 1009; // 0x3f1 field public static final int TYPE_REMOTE_SPEAKER = 1002; // 0x3ea - field public static final int TYPE_REMOTE_TABLET = 1004; // 0x3ec - field public static final int TYPE_REMOTE_TABLET_DOCKED = 1005; // 0x3ed + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_TABLET = 1004; // 0x3ec + field @FlaggedApi("com.android.media.flags.enable_new_media_route_2_info_types") public static final int TYPE_REMOTE_TABLET_DOCKED = 1005; // 0x3ed field public static final int TYPE_REMOTE_TV = 1001; // 0x3e9 field public static final int TYPE_UNKNOWN = 0; // 0x0 field public static final int TYPE_USB_ACCESSORY = 12; // 0xc @@ -42599,6 +42599,7 @@ package android.telecom { field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE"; field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE"; field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION"; + field @FlaggedApi("com.android.server.telecom.flags.add_call_uri_for_missed_calls") public static final String EXTRA_CALL_LOG_URI = "android.telecom.extra.CALL_LOG_URI"; field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE"; field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT"; field public static final String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME"; @@ -45537,6 +45538,7 @@ package android.telephony { field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2; // 0x2 field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9; // 0x9 field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6; // 0x6 + field @FlaggedApi("com.android.internal.telephony.flags.slicing_additional_error_codes") public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16; // 0x10 field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2 field public static final int SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE = 3; // 0x3 field public static final int SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION = 4; // 0x4 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 5bce8b291402..183b925cefd5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -37,7 +37,7 @@ package android { field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS"; field public static final String ALLOW_SLIPPERY_TOUCHES = "android.permission.ALLOW_SLIPPERY_TOUCHES"; - field public static final String ALWAYS_UPDATE_WALLPAPER = "android.permission.ALWAYS_UPDATE_WALLPAPER"; + field @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission") public static final String ALWAYS_UPDATE_WALLPAPER = "android.permission.ALWAYS_UPDATE_WALLPAPER"; field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER"; field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES"; @@ -627,6 +627,7 @@ package android.app { field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service"; field public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android:capture_consentless_bugreport_on_userdebug_build"; field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state"; + field @FlaggedApi("android.view.contentprotection.flags.create_accessibility_overlay_app_op_enabled") public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY = "android:create_accessibility_overlay"; field public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager"; field public static final String OPSTR_ESTABLISH_VPN_SERVICE = "android:establish_vpn_service"; field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 7e84ceb4a61d..b03bd59c305e 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,10 +16,13 @@ package android.app; +import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED; + import static java.lang.Long.max; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -1495,9 +1498,17 @@ public class AppOpsManager { public static final int OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA = AppProtoEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA; + /** + * Creation of an overlay using accessibility services + * + * @hide + */ + public static final int OP_CREATE_ACCESSIBILITY_OVERLAY = + AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 138; + public static final int _NUM_OP = 139; /** * All app ops represented as strings. @@ -1641,7 +1652,8 @@ public class AppOpsManager { OPSTR_CAMERA_SANDBOXED, OPSTR_RECORD_AUDIO_SANDBOXED, OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO, - OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA + OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA, + OPSTR_CREATE_ACCESSIBILITY_OVERLAY, }) public @interface AppOpString {} @@ -2270,6 +2282,16 @@ public class AppOpsManager { public static final String OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA = "android:RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA"; + /** + * Creation of an overlay using accessibility services + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED) + public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY = + "android:create_accessibility_overlay"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -2819,7 +2841,11 @@ public class AppOpsManager { OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA, "RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA") .setPermission(Manifest.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA) - .setDefaultMode(AppOpsManager.MODE_DEFAULT).build() + .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(), + new AppOpInfo.Builder(OP_CREATE_ACCESSIBILITY_OVERLAY, + OPSTR_CREATE_ACCESSIBILITY_OVERLAY, + "CREATE_ACCESSIBILITY_OVERLAY") + .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), }; // The number of longs needed to form a full bitmask of app ops diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index fd308ce2e85a..367e92b9d960 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -48,7 +48,7 @@ import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApkChecksum; import android.content.pm.ApplicationInfo; -import android.content.pm.ArchivedPackage; +import android.content.pm.ArchivedPackageInfo; import android.content.pm.ChangedPackages; import android.content.pm.Checksum; import android.content.pm.ComponentInfo; @@ -3937,13 +3937,13 @@ public class ApplicationPackageManager extends PackageManager { } @Override - public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) { + public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) { try { var parcel = mPM.getArchivedPackage(packageName, mContext.getUserId()); if (parcel == null) { return null; } - return new ArchivedPackage(parcel); + return new ArchivedPackageInfo(parcel); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 9cf54e3b9063..cc56a1cee7a2 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -36,6 +36,7 @@ per-file GameMode* = file:/GAME_MANAGER_OWNERS per-file GameState* = file:/GAME_MANAGER_OWNERS per-file IGameManager* = file:/GAME_MANAGER_OWNERS per-file IGameMode* = file:/GAME_MANAGER_OWNERS +per-file BackgroundStartPrivileges.java = file:/BAL_OWNERS # ActivityThread per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 7704486b606c..a46c1006d314 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -49,6 +49,7 @@ import static android.Manifest.permission.QUERY_ADMIN_POLICY; import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY; import static android.Manifest.permission.SET_TIME; import static android.Manifest.permission.SET_TIME_ZONE; +import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; @@ -17136,4 +17137,14 @@ public class DevicePolicyManager { } return null; } -} + + // TODO(b/308755220): Remove once the build is finalised. + /** + * Returns true if the flag for the onboarding bugreport V2 is enabled. + * + * @hide + */ + public boolean isOnboardingBugreportV2FlagEnabled() { + return onboardingBugreportV2Enabled(); + } +}
\ No newline at end of file diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 3abe1d9494f0..c6012bbc7292 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -562,13 +562,6 @@ public class AppWidgetManager { }); } - private boolean isPostingTaskToBackground(@Nullable RemoteViews views) { - return Looper.myLooper() == Looper.getMainLooper() - && RemoteViews.isAdapterConversionEnabled() - && (mHasPostedLegacyLists = mHasPostedLegacyLists - || (views != null && views.hasLegacyLists())); - } - /** * Set the RemoteViews to use for the specified appWidgetIds. * <p> @@ -593,16 +586,25 @@ public class AppWidgetManager { return; } - if (isPostingTaskToBackground(views)) { - createUpdateExecutorIfNull().execute(() -> { - try { - mService.updateAppWidgetIds(mPackageName, appWidgetIds, views); - } catch (RemoteException e) { - Log.e(TAG, "Error updating app widget views in background", e); - } - }); + final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() + && (mHasPostedLegacyLists = mHasPostedLegacyLists + || (views != null && views.hasLegacyLists())); - return; + if (isConvertingAdapter) { + views.collectAllIntents(); + + if (Looper.getMainLooper() == Looper.myLooper()) { + RemoteViews viewsCopy = new RemoteViews(views); + createUpdateExecutorIfNull().execute(() -> { + try { + mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy); + } catch (RemoteException e) { + Log.e(TAG, "Error updating app widget views in background", e); + } + }); + + return; + } } try { @@ -714,16 +716,25 @@ public class AppWidgetManager { return; } - if (isPostingTaskToBackground(views)) { - createUpdateExecutorIfNull().execute(() -> { - try { - mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views); - } catch (RemoteException e) { - Log.e(TAG, "Error partially updating app widget views in background", e); - } - }); + final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() + && (mHasPostedLegacyLists = mHasPostedLegacyLists + || (views != null && views.hasLegacyLists())); + + if (isConvertingAdapter) { + views.collectAllIntents(); - return; + if (Looper.getMainLooper() == Looper.myLooper()) { + RemoteViews viewsCopy = new RemoteViews(views); + createUpdateExecutorIfNull().execute(() -> { + try { + mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy); + } catch (RemoteException e) { + Log.e(TAG, "Error partially updating app widget views in background", e); + } + }); + + return; + } } try { @@ -782,16 +793,26 @@ public class AppWidgetManager { return; } - if (isPostingTaskToBackground(views)) { - createUpdateExecutorIfNull().execute(() -> { - try { - mService.updateAppWidgetProvider(provider, views); - } catch (RemoteException e) { - Log.e(TAG, "Error updating app widget view using provider in background", e); - } - }); + final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled() + && (mHasPostedLegacyLists = mHasPostedLegacyLists + || (views != null && views.hasLegacyLists())); + + if (isConvertingAdapter) { + views.collectAllIntents(); - return; + if (Looper.getMainLooper() == Looper.myLooper()) { + RemoteViews viewsCopy = new RemoteViews(views); + createUpdateExecutorIfNull().execute(() -> { + try { + mService.updateAppWidgetProvider(provider, viewsCopy); + } catch (RemoteException e) { + Log.e(TAG, "Error updating app widget view using provider in background", + e); + } + }); + + return; + } } try { diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index d40a5911d8f4..b3ea93bb8a85 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -185,9 +185,6 @@ public final class VirtualDeviceManager { int associationId, @NonNull VirtualDeviceParams params) { Objects.requireNonNull(params, "params must not be null"); - if (Flags.moreLogs()) { - Log.i(TAG, "Creating VirtualDevice"); - } try { return new VirtualDevice(mService, mContext, associationId, params); } catch (RemoteException e) { diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index f380963fbcab..3cadb7c09d54 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -1,13 +1,6 @@ package: "android.companion.virtual.flags" flag { - name: "more_logs" - namespace: "virtual_devices" - description: "More logs to test flags with" - bug: "291725823" -} - -flag { name: "enable_native_vdm" namespace: "virtual_devices" description: "Enable native VDM service" diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ffc4805df264..ea54c912d4b9 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1164,6 +1164,11 @@ public class Intent implements Parcelable, Cloneable { * numbers. Applications can <strong>dial</strong> emergency numbers using * {@link #ACTION_DIAL}, however. * + * <p>Note: This Intent can only be used to dial call forwarding MMI codes if the application + * using this intent is set as the default or system dialer. The system will treat any other + * application using this Intent for the purpose of dialing call forwarding MMI codes as if the + * {@link #ACTION_DIAL} Intent was used instead. + * * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than * relying on this intent. diff --git a/core/java/android/content/pm/ArchivedActivity.java b/core/java/android/content/pm/ArchivedActivityInfo.java index 9e49c9e52878..1faa4373d88f 100644 --- a/core/java/android/content/pm/ArchivedActivity.java +++ b/core/java/android/content/pm/ArchivedActivityInfo.java @@ -32,9 +32,13 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Objects; +/** + * Contains fields required to show archived package in Launcher. + * @see ArchivedPackageInfo + */ @DataClass(genBuilder = false, genConstructor = false, genSetters = true) @FlaggedApi(Flags.FLAG_ARCHIVING) -public final class ArchivedActivity { +public final class ArchivedActivityInfo { /** The label for the activity. */ private @NonNull CharSequence mLabel; /** The component name of this activity. */ @@ -47,7 +51,7 @@ public final class ArchivedActivity { /** Monochrome icon, if defined, of the activity. */ private @Nullable Drawable mMonochromeIcon; - public ArchivedActivity(@NonNull CharSequence label, @NonNull ComponentName componentName) { + public ArchivedActivityInfo(@NonNull CharSequence label, @NonNull ComponentName componentName) { Objects.requireNonNull(label); Objects.requireNonNull(componentName); mLabel = label; @@ -55,7 +59,7 @@ public final class ArchivedActivity { } /* @hide */ - ArchivedActivity(@NonNull ArchivedActivityParcel parcel) { + ArchivedActivityInfo(@NonNull ArchivedActivityParcel parcel) { mLabel = parcel.title; mComponentName = parcel.originalComponentName; mIcon = drawableFromCompressedBitmap(parcel.iconBitmap); @@ -149,7 +153,7 @@ public final class ArchivedActivity { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivity.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -193,7 +197,7 @@ public final class ArchivedActivity { * The label for the activity. */ @DataClass.Generated.Member - public @NonNull ArchivedActivity setLabel(@NonNull CharSequence value) { + public @NonNull ArchivedActivityInfo setLabel(@NonNull CharSequence value) { mLabel = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mLabel); @@ -204,7 +208,7 @@ public final class ArchivedActivity { * The component name of this activity. */ @DataClass.Generated.Member - public @NonNull ArchivedActivity setComponentName(@NonNull ComponentName value) { + public @NonNull ArchivedActivityInfo setComponentName(@NonNull ComponentName value) { mComponentName = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mComponentName); @@ -216,7 +220,7 @@ public final class ArchivedActivity { * launcher. */ @DataClass.Generated.Member - public @NonNull ArchivedActivity setIcon(@NonNull Drawable value) { + public @NonNull ArchivedActivityInfo setIcon(@NonNull Drawable value) { mIcon = value; return this; } @@ -225,16 +229,16 @@ public final class ArchivedActivity { * Monochrome icon, if defined, of the activity. */ @DataClass.Generated.Member - public @NonNull ArchivedActivity setMonochromeIcon(@NonNull Drawable value) { + public @NonNull ArchivedActivityInfo setMonochromeIcon(@NonNull Drawable value) { mMonochromeIcon = value; return this; } @DataClass.Generated( - time = 1698173429911L, + time = 1698789991876L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivity.java", - inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivity extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)") + sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java", + inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/ArchivedPackage.java b/core/java/android/content/pm/ArchivedPackageInfo.java index 42795db35684..f432598ef887 100644 --- a/core/java/android/content/pm/ArchivedPackage.java +++ b/core/java/android/content/pm/ArchivedPackageInfo.java @@ -27,9 +27,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +/** + * Contains fields required for archived package installation, + * i.e. installation without an APK. + */ @DataClass(genBuilder = false, genConstructor = false, genSetters = true) @FlaggedApi(Flags.FLAG_ARCHIVING) -public final class ArchivedPackage { +public final class ArchivedPackageInfo { /** Name of the package as used to identify it in the system */ private @NonNull String mPackageName; /** Signing certificates used to sign the package. */ @@ -74,10 +78,10 @@ public final class ArchivedPackage { * {@link Intent#CATEGORY_LAUNCHER}. * @see LauncherApps#getActivityList */ - private @NonNull List<ArchivedActivity> mLauncherActivities; + private @NonNull List<ArchivedActivityInfo> mLauncherActivities; - public ArchivedPackage(@NonNull String packageName, @NonNull SigningInfo signingInfo, - @NonNull List<ArchivedActivity> launcherActivities) { + public ArchivedPackageInfo(@NonNull String packageName, @NonNull SigningInfo signingInfo, + @NonNull List<ArchivedActivityInfo> launcherActivities) { Objects.requireNonNull(packageName); Objects.requireNonNull(signingInfo); Objects.requireNonNull(launcherActivities); @@ -90,7 +94,7 @@ public final class ArchivedPackage { * Constructs the archived package from parcel. * @hide */ - public ArchivedPackage(@NonNull ArchivedPackageParcel parcel) { + public ArchivedPackageInfo(@NonNull ArchivedPackageParcel parcel) { mPackageName = parcel.packageName; mSigningInfo = new SigningInfo(parcel.signingDetails); mVersionCode = parcel.versionCode; @@ -102,7 +106,7 @@ public final class ArchivedPackage { mLauncherActivities = new ArrayList<>(); if (parcel.archivedActivities != null) { for (var activityParcel : parcel.archivedActivities) { - mLauncherActivities.add(new ArchivedActivity(activityParcel)); + mLauncherActivities.add(new ArchivedActivityInfo(activityParcel)); } } } @@ -135,7 +139,7 @@ public final class ArchivedPackage { // CHECKSTYLE:OFF Generated code // // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackage.java + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackageInfo.java // // To exclude the generated code from IntelliJ auto-formatting enable (one-time): // Settings > Editor > Code Style > Formatter Control @@ -224,7 +228,7 @@ public final class ArchivedPackage { * @see LauncherApps#getActivityList */ @DataClass.Generated.Member - public @NonNull List<ArchivedActivity> getLauncherActivities() { + public @NonNull List<ArchivedActivityInfo> getLauncherActivities() { return mLauncherActivities; } @@ -232,7 +236,7 @@ public final class ArchivedPackage { * Name of the package as used to identify it in the system */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setPackageName(@NonNull String value) { + public @NonNull ArchivedPackageInfo setPackageName(@NonNull String value) { mPackageName = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mPackageName); @@ -243,7 +247,7 @@ public final class ArchivedPackage { * Signing certificates used to sign the package. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setSigningInfo(@NonNull SigningInfo value) { + public @NonNull ArchivedPackageInfo setSigningInfo(@NonNull SigningInfo value) { mSigningInfo = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSigningInfo); @@ -255,7 +259,7 @@ public final class ArchivedPackage { * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setVersionCode( int value) { + public @NonNull ArchivedPackageInfo setVersionCode( int value) { mVersionCode = value; return this; } @@ -265,7 +269,7 @@ public final class ArchivedPackage { * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setVersionCodeMajor( int value) { + public @NonNull ArchivedPackageInfo setVersionCodeMajor( int value) { mVersionCodeMajor = value; return this; } @@ -276,7 +280,7 @@ public final class ArchivedPackage { * attribute. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setTargetSdkVersion( int value) { + public @NonNull ArchivedPackageInfo setTargetSdkVersion( int value) { mTargetSdkVersion = value; return this; } @@ -287,7 +291,7 @@ public final class ArchivedPackage { * attribute. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String value) { + public @NonNull ArchivedPackageInfo setDefaultToDeviceProtectedStorage(@NonNull String value) { mDefaultToDeviceProtectedStorage = value; return this; } @@ -299,7 +303,7 @@ public final class ArchivedPackage { * attribute. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setRequestLegacyExternalStorage(@NonNull String value) { + public @NonNull ArchivedPackageInfo setRequestLegacyExternalStorage(@NonNull String value) { mRequestLegacyExternalStorage = value; return this; } @@ -310,7 +314,7 @@ public final class ArchivedPackage { * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute. */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setUserDataFragile(@NonNull String value) { + public @NonNull ArchivedPackageInfo setUserDataFragile(@NonNull String value) { mUserDataFragile = value; return this; } @@ -322,7 +326,7 @@ public final class ArchivedPackage { * @see LauncherApps#getActivityList */ @DataClass.Generated.Member - public @NonNull ArchivedPackage setLauncherActivities(@NonNull List<ArchivedActivity> value) { + public @NonNull ArchivedPackageInfo setLauncherActivities(@NonNull List<ArchivedActivityInfo> value) { mLauncherActivities = value; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mLauncherActivities); @@ -330,10 +334,10 @@ public final class ArchivedPackage { } @DataClass.Generated( - time = 1697824890503L, + time = 1698789995536L, codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackage.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate int mVersionCode\nprivate int mVersionCodeMajor\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivity> mLauncherActivities\n android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackage extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)") + sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackageInfo.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate int mVersionCode\nprivate int mVersionCodeMajor\nprivate int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivityInfo> mLauncherActivities\n android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackageInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 62dda6b02241..1114b358e08a 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1006,7 +1006,7 @@ public class PackageInstaller { /** * Install package in an archived state. * - * @param archivedPackage archived package data such as package name, signature etc. + * @param archivedPackageInfo archived package data such as package name, signature etc. * @param sessionParams used to create an underlying installation session * @param statusReceiver Called when the state of the session changes. Intents * sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the @@ -1016,15 +1016,15 @@ public class PackageInstaller { */ @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) @FlaggedApi(Flags.FLAG_ARCHIVING) - public void installPackageArchived(@NonNull ArchivedPackage archivedPackage, + public void installPackageArchived(@NonNull ArchivedPackageInfo archivedPackageInfo, @NonNull SessionParams sessionParams, @NonNull IntentSender statusReceiver) { - Objects.requireNonNull(archivedPackage, "archivedPackage cannot be null"); + Objects.requireNonNull(archivedPackageInfo, "archivedPackageInfo cannot be null"); Objects.requireNonNull(sessionParams, "sessionParams cannot be null"); Objects.requireNonNull(statusReceiver, "statusReceiver cannot be null"); try { mInstaller.installPackageArchived( - archivedPackage.getParcel(), + archivedPackageInfo.getParcel(), sessionParams, statusReceiver, mInstallerPackageName, diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ad7dd513b382..dea4a12541e5 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -11032,7 +11032,7 @@ public abstract class PackageManager { * @see PackageInstaller#installPackageArchived */ @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING) - public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) { + public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) { throw new UnsupportedOperationException( "getArchivedPackage not implemented in subclass"); } diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java index 524afe975d73..ad3ccc41cf6d 100644 --- a/core/java/android/credentials/CredentialManager.java +++ b/core/java/android/credentials/CredentialManager.java @@ -437,7 +437,14 @@ public final class CredentialManager { * Returns {@code true} if the calling application provides a CredentialProviderService that is * enabled for the current user, or {@code false} otherwise. CredentialProviderServices are * enabled on a per-service basis so the individual component name of the service should be - * passed in here. + * passed in here. <strong>Usage of this API is discouraged as it is not fully functional, and + * may throw a NullPointerException on certain devices and/or API versions.</strong> + * + * @throws IllegalArgumentException if the componentName package does not match the calling + * package name this call will throw an exception + * + * @throws NullPointerException Usage of this API is discouraged as it is not fully + * functional, and may throw a NullPointerException on certain devices and/or API versions * * @param componentName the component name to check is enabled */ diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index e37b2b5ef3ef..677143afd4fb 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -730,13 +730,14 @@ public class Process { whitelistedDataInfoMap, boolean bindMountAppsData, boolean bindMountAppStorageDirs, + boolean bindMountSystemOverrides, @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, whitelistedDataInfoMap, bindMountAppsData, - bindMountAppStorageDirs, zygoteArgs); + bindMountAppStorageDirs, bindMountSystemOverrides, zygoteArgs); } /** @hide */ @@ -753,6 +754,7 @@ public class Process { @Nullable String invokeWith, @Nullable String packageName, @Nullable long[] disabledCompatChanges, + boolean bindMountSyspropOverrides, @Nullable String[] zygoteArgs) { // Webview zygote can't access app private data files, so doesn't need to know its data // info. @@ -761,7 +763,8 @@ public class Process { abi, instructionSet, appDataDir, invokeWith, packageName, /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false, disabledCompatChanges, /* pkgDataInfoMap */ null, - /* whitelistedDataInfoMap */ null, false, false, zygoteArgs); + /* whitelistedDataInfoMap */ null, /* bindMountAppsData */ false, + /* bindMountAppStorageDirs */ false, bindMountSyspropOverrides, zygoteArgs); } /** diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 3cb5c60259eb..c14810bbcd64 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -355,6 +355,7 @@ public class ZygoteProcess { allowlistedDataInfoList, boolean bindMountAppsData, boolean bindMountAppStorageDirs, + boolean bindOverrideSysprops, @Nullable String[] zygoteArgs) { // TODO (chriswailes): Is there a better place to check this value? if (fetchUsapPoolEnabledPropWithMinInterval()) { @@ -367,7 +368,7 @@ public class ZygoteProcess { abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false, packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData, - bindMountAppStorageDirs, zygoteArgs); + bindMountAppStorageDirs, bindOverrideSysprops, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -638,6 +639,7 @@ public class ZygoteProcess { allowlistedDataInfoList, boolean bindMountAppsData, boolean bindMountAppStorageDirs, + boolean bindMountOverrideSysprops, @Nullable String[] extraArgs) throws ZygoteStartFailedEx { ArrayList<String> argsForZygote = new ArrayList<>(); @@ -753,6 +755,10 @@ public class ZygoteProcess { argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS); } + if (bindMountOverrideSysprops) { + argsForZygote.add(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES); + } + if (disabledCompatChanges != null && disabledCompatChanges.length > 0) { StringBuilder sb = new StringBuilder(); sb.append("--disabled-compat-changes="); @@ -1306,7 +1312,8 @@ public class ZygoteProcess { ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */, null /* disabledCompatChanges */, null /* pkgDataInfoMap */, null /* allowlistedDataInfoList */, true /* bindMountAppsData*/, - /* bindMountAppStorageDirs */ false, extraArgs); + /* bindMountAppStorageDirs */ false, /*bindMountOverrideSysprops */ false, + extraArgs); } catch (ZygoteStartFailedEx ex) { throw new RuntimeException("Starting child-zygote through Zygote failed", ex); diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 27ad45de69e6..bcda25a1bf3b 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -3206,6 +3206,15 @@ public final class Telephony { public static final String INFRASTRUCTURE_BITMASK = "infrastructure_bitmask"; /** + * Indicating if the APN is used for eSIM bootsrap provisioning. The default value is 0 (Not + * used for eSIM bootstrap provisioning). + * + * <P>Type: INTEGER</P> + * @hide + */ + public static final String ESIM_BOOTSTRAP_PROVISIONING = "esim_bootstrap_provisioning"; + + /** * MVNO type: * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}. * <P>Type: TEXT</P> diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig index 43c38f319713..a1885ae616e1 100644 --- a/core/java/android/text/flags/flags.aconfig +++ b/core/java/android/text/flags/flags.aconfig @@ -75,3 +75,10 @@ flag { description: "A feature flag that implements line break word style auto." bug: "280005585" } + +flag { + name: "inter_character_justification" + namespace: "text" + description: "A feature flag that implement inter character justification." + bug: "283193133" +} diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java index 52be8f6a76fd..127d4a70a564 100644 --- a/core/java/android/view/SurfaceControlRegistry.java +++ b/core/java/android/view/SurfaceControlRegistry.java @@ -337,13 +337,13 @@ public class SurfaceControlRegistry { @VisibleForTesting public final boolean matchesForCallStackDebugging(@Nullable String name, @NonNull String call) { final boolean matchCall = !sCallStackDebuggingMatchCall.isEmpty(); - if (matchCall && !call.toLowerCase().contains(sCallStackDebuggingMatchCall)) { + if (matchCall && !sCallStackDebuggingMatchCall.contains(call.toLowerCase())) { // Skip if target call doesn't match requested caller return false; } final boolean matchName = !sCallStackDebuggingMatchName.isEmpty(); if (matchName && (name == null - || !name.toLowerCase().contains(sCallStackDebuggingMatchName))) { + || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) { // Skip if target surface doesn't match requested surface return false; } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 5b69d7fdeb35..0ae14a2fdb30 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -43,6 +43,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; +import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -51,6 +52,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityEmbeddedConnection; import android.window.SurfaceSyncGroup; +import com.android.graphics.hwui.flags.Flags; import com.android.internal.view.SurfaceCallbackHelper; import java.lang.annotation.Retention; @@ -326,6 +328,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } }; + private final boolean mRtDrivenClipping = Flags.clipSurfaceviews(); + public SurfaceView(Context context) { this(context, null); } @@ -572,6 +576,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall public void setClipBounds(Rect clipBounds) { super.setClipBounds(clipBounds); + if (mRtDrivenClipping && isHardwareAccelerated()) { + return; + } + if (!mClipSurfaceToBounds || mSurfaceControl == null) { return; } @@ -915,15 +923,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } if (sizeChanged || creating || !isHardwareAccelerated()) { - // Set a window crop when creating the surface or changing its size to - // crop the buffer to the surface size since the buffer producer may - // use SCALING_MODE_SCALE and submit a larger size than the surface - // size. - if (mClipSurfaceToBounds && mClipBounds != null) { - surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds); - } else { - surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, - mSurfaceHeight); + if (!mRtDrivenClipping || !isHardwareAccelerated()) { + // Set a window crop when creating the surface or changing its size to + // crop the buffer to the surface size since the buffer producer may + // use SCALING_MODE_SCALE and submit a larger size than the surface + // size. + if (mClipSurfaceToBounds && mClipBounds != null) { + surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds); + } else { + surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, + mSurfaceHeight); + } } surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth, @@ -941,7 +951,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/); } if (DEBUG_POSITION) { - Log.d(TAG, String.format( + Log.d(TAG, TextUtils.formatSimple( "%d performSurfaceTransaction %s " + "position = [%d, %d, %d, %d] surfaceSize = %dx%d", System.identityHashCode(this), @@ -1453,6 +1463,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private final Rect mRTLastReportedPosition = new Rect(); + private final Rect mRTLastSetCrop = new Rect(); private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener { private final int mRtSurfaceWidth; @@ -1496,6 +1507,45 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } @Override + public void positionChanged(long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + try { + if (DEBUG_POSITION) { + Log.d(TAG, String.format( + "%d updateSurfacePosition RenderWorker, frameNr = %d, " + + "position = [%d, %d, %d, %d] clip = [%d, %d, %d, %d] " + + "surfaceSize = %dx%d", + System.identityHashCode(SurfaceView.this), frameNumber, + left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom, + mRtSurfaceWidth, mRtSurfaceHeight)); + } + synchronized (mSurfaceControlLock) { + if (mSurfaceControl == null) return; + + mRTLastReportedPosition.set(left, top, right, bottom); + onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl, + mRTLastReportedPosition.left /*positionLeft*/, + mRTLastReportedPosition.top /*positionTop*/, + mRTLastReportedPosition.width() + / (float) mRtSurfaceWidth /*postScaleX*/, + mRTLastReportedPosition.height() + / (float) mRtSurfaceHeight /*postScaleY*/); + + mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom); + mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop); + if (mRTLastSetCrop.isEmpty()) { + mPositionChangedTransaction.hide(mSurfaceControl); + } else { + mPositionChangedTransaction.show(mSurfaceControl); + } + } + applyOrMergeTransaction(mPositionChangedTransaction, frameNumber); + } catch (Exception ex) { + Log.e(TAG, "Exception from repositionChild", ex); + } + } + + @Override public void applyStretch(long frameNumber, float width, float height, float vecX, float vecY, float maxStretchX, float maxStretchY, float childRelativeLeft, float childRelativeTop, float childRelativeRight, diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 50f9bc423ba9..24223694e421 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3964,9 +3964,15 @@ public final class ViewRootImpl implements ViewParent, // on a different thread. However, when the current process is system, the finishDraw in // system server will be run on the current thread, which could result in a deadlock. if (mWindowSession instanceof Binder) { - reportDrawFinished(t, seqId); + // The transaction should be copied to a local reference when posting onto a new + // thread because up until now the SSG is holding a lock on the transaction. Once + // the call jumps onto a new thread, the lock is no longer held and the transaction + // send back may be modified or used again. + Transaction transactionCopy = new Transaction(); + transactionCopy.merge(t); + mHandler.postAtFrontOfQueue(() -> reportDrawFinished(transactionCopy, seqId)); } else { - mHandler.postAtFrontOfQueue(() -> reportDrawFinished(t, seqId)); + reportDrawFinished(t, seqId); } }); if (DEBUG_BLAST) { @@ -3977,8 +3983,17 @@ public final class ViewRootImpl implements ViewParent, } private void notifyContentCaptureEvents() { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); try { + if (!isContentCaptureEnabled()) { + if (DEBUG_CONTENT_CAPTURE) { + Log.d(mTag, "notifyContentCaptureEvents while disabled"); + } + mAttachInfo.mContentCaptureEvents = null; + return; + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); + } MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager .getMainContentCaptureSession(); for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { @@ -4892,13 +4907,14 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_CONTENT_CAPTURE) { Log.v(mTag, "performContentCaptureInitialReport() on " + rootView); } - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " - + getClass().getSimpleName()); - } try { if (!isContentCaptureEnabled()) return; + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " + + getClass().getSimpleName()); + } + // Initial dispatch of window bounds to content capture if (mAttachInfo.mContentCaptureManager != null) { MainContentCaptureSession session = @@ -4918,13 +4934,14 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_CONTENT_CAPTURE) { Log.v(mTag, "handleContentCaptureFlush()"); } - if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " - + getClass().getSimpleName()); - } try { if (!isContentCaptureEnabled()) return; + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " + + getClass().getSimpleName()); + } + final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager; if (ccm == null) { Log.w(TAG, "No ContentCapture on AttachInfo"); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 970baf2ce264..5a058ff3de99 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -794,7 +794,9 @@ public final class ContentCaptureManager { (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0; MainContentCaptureSession mainSession; + boolean alreadyDisabledByApp; synchronized (mLock) { + alreadyDisabledByApp = (mFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0; if (flagSecureEnabled) { mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; } else { @@ -802,7 +804,9 @@ public final class ContentCaptureManager { } mainSession = mMainSession; } - if (mainSession != null) { + + // Prevent overriding the status of disabling by app + if (mainSession != null && !alreadyDisabledByApp) { mainSession.setDisabled(flagSecureEnabled); } } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index b44d6a496058..d9b0f8035a6d 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -44,6 +44,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; +import android.os.Trace; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; @@ -373,12 +374,26 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return; } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + if (eventType == TYPE_VIEW_TREE_APPEARING) { + Trace.asyncTraceBegin( + Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0); + } + } + if (isContentProtectionReceiverEnabled()) { sendContentProtectionEvent(event); } if (isContentCaptureReceiverEnabled()) { sendContentCaptureEvent(event, forceFlush); } + + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + if (eventType == TYPE_VIEW_TREE_APPEARED) { + Trace.asyncTraceEnd( + Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0); + } + } } @UiThread diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig index f6ee061fdd89..f3dc33cd2cc9 100644 --- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig +++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig @@ -20,3 +20,10 @@ flag { description: "If true, content protection setting ui is displayed in Settings > Privacy & Security > More security & privacy." bug: "305792348" } + +flag { + name: "create_accessibility_overlay_app_op_enabled" + namespace: "content_protection" + description: "If true, an appop is logged on creation of accessibility overlays." + bug: "289081465" +} diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 30788014ff92..403b403be961 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -85,6 +85,7 @@ import android.util.Log; import android.util.LongArray; import android.util.Pair; import android.util.SizeF; +import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; @@ -367,6 +368,11 @@ public class RemoteViews implements Parcelable, Filter { @UnsupportedAppUsage private BitmapCache mBitmapCache = new BitmapCache(); + /** + * Maps Intent ID to RemoteCollectionItems to avoid duplicate items + */ + private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache(); + /** Cache of ApplicationInfos used by collection items. */ private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache(); @@ -784,9 +790,12 @@ public class RemoteViews implements Parcelable, Filter { if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction && itemsAction.mViewId == viewId && itemsAction.mServiceIntent != null) { - mActions.set(i, - new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId, - itemsAction.mServiceIntent)); + SetRemoteCollectionItemListAdapterAction newCollectionAction = + new SetRemoteCollectionItemListAdapterAction( + itemsAction.mViewId, itemsAction.mServiceIntent); + newCollectionAction.mIntentId = itemsAction.mIntentId; + newCollectionAction.mIsReplacedIntoAction = true; + mActions.set(i, newCollectionAction); isActionReplaced = true; } else if (action instanceof SetRemoteViewsAdapterIntent intentAction && intentAction.mViewId == viewId) { @@ -1048,6 +1057,8 @@ public class RemoteViews implements Parcelable, Filter { @NonNull private CompletableFuture<RemoteCollectionItems> mItemsFuture; final Intent mServiceIntent; + int mIntentId = -1; + boolean mIsReplacedIntoAction = false; SetRemoteCollectionItemListAdapterAction(@IdRes int id, @NonNull RemoteCollectionItems items) { @@ -1108,38 +1119,36 @@ public class RemoteViews implements Parcelable, Filter { SetRemoteCollectionItemListAdapterAction(Parcel parcel) { mViewId = parcel.readInt(); - mItemsFuture = CompletableFuture.completedFuture( - new RemoteCollectionItems(parcel, getHierarchyRootData())); + mIntentId = parcel.readInt(); + mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1 + ? null + : new RemoteCollectionItems(parcel, getHierarchyRootData())); mServiceIntent = parcel.readTypedObject(Intent.CREATOR); } @Override public void setHierarchyRootData(HierarchyRootData rootData) { - mItemsFuture = mItemsFuture - .thenApply(rc -> { - rc.setHierarchyRootData(rootData); - return rc; - }); - } - - private static RemoteCollectionItems getCollectionItemsFromFuture( - CompletableFuture<RemoteCollectionItems> itemsFuture) { - RemoteCollectionItems items; - try { - items = itemsFuture.get(); - } catch (Exception e) { - Log.e(LOG_TAG, "Error getting collection items from future", e); - items = new RemoteCollectionItems.Builder().build(); + if (mIntentId == -1) { + mItemsFuture = mItemsFuture + .thenApply(rc -> { + rc.setHierarchyRootData(rootData); + return rc; + }); + return; } - return items; + // Set the root data for items in the cache instead + mCollectionCache.setHierarchyDataForId(mIntentId, rootData); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewId); - RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); - items.writeToParcel(dest, flags, /* attached= */ true); + dest.writeInt(mIntentId); + if (mIntentId == -1) { + RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); + items.writeToParcel(dest, flags, /* attached= */ true); + } dest.writeTypedObject(mServiceIntent, flags); } @@ -1149,7 +1158,9 @@ public class RemoteViews implements Parcelable, Filter { View target = root.findViewById(mViewId); if (target == null) return; - RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture); + RemoteCollectionItems items = mIntentId == -1 + ? getCollectionItemsFromFuture(mItemsFuture) + : mCollectionCache.getItemsForId(mIntentId); // Ensure that we are applying to an AppWidget root if (!(rootParent instanceof AppWidgetHostView)) { @@ -1210,6 +1221,153 @@ public class RemoteViews implements Parcelable, Filter { } } + private static RemoteCollectionItems getCollectionItemsFromFuture( + CompletableFuture<RemoteCollectionItems> itemsFuture) { + RemoteCollectionItems items; + try { + items = itemsFuture.get(); + } catch (Exception e) { + Log.e(LOG_TAG, "Error getting collection items from future", e); + items = new RemoteCollectionItems.Builder().build(); + } + + return items; + } + + /** + * @hide + */ + public void collectAllIntents() { + mCollectionCache.collectAllIntentsNoComplete(this); + } + + private class RemoteCollectionCache { + private SparseArray<String> mIdToUriMapping = new SparseArray<>(); + private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>(); + + // We don't put this into the parcel + private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping = + new HashMap<>(); + + RemoteCollectionCache() { } + + RemoteCollectionCache(RemoteCollectionCache src) { + boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0; + for (int i = 0; i < src.mIdToUriMapping.size(); i++) { + String uri = src.mIdToUriMapping.valueAt(i); + mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri); + if (isWaitingCache) { + mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri)); + } else { + mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri)); + } + } + } + + RemoteCollectionCache(Parcel in) { + int cacheSize = in.readInt(); + HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache, + this, + mApplicationInfoCache, + mClassCookies); + for (int i = 0; i < cacheSize; i++) { + int intentId = in.readInt(); + String intentUri = in.readString8(); + RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData); + mIdToUriMapping.put(intentId, intentUri); + mUriToCollectionMapping.put(intentUri, items); + } + } + + void setHierarchyDataForId(int intentId, HierarchyRootData data) { + String uri = mIdToUriMapping.get(intentId); + if (mTempUriToFutureMapping.get(uri) != null) { + CompletableFuture<RemoteCollectionItems> itemsFuture = + mTempUriToFutureMapping.get(uri); + mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> { + rc.setHierarchyRootData(data); + return rc; + })); + + return; + } + + RemoteCollectionItems items = mUriToCollectionMapping.get(uri); + items.setHierarchyRootData(data); + } + + RemoteCollectionItems getItemsForId(int intentId) { + String uri = mIdToUriMapping.get(intentId); + return mUriToCollectionMapping.get(uri); + } + + void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) { + if (inViews.hasSizedRemoteViews()) { + for (RemoteViews remoteViews : inViews.mSizedRemoteViews) { + remoteViews.collectAllIntents(); + } + } else if (inViews.hasLandscapeAndPortraitLayouts()) { + inViews.mLandscape.collectAllIntents(); + inViews.mPortrait.collectAllIntents(); + } else if (inViews.mActions != null) { + for (Action action : inViews.mActions) { + if (action instanceof SetRemoteCollectionItemListAdapterAction rca) { + // Deal with the case where the intent is replaced into the action list + if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) { + continue; + } + + if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) { + String uri = mIdToUriMapping.get(rca.mIntentId); + mTempUriToFutureMapping.put(uri, rca.mItemsFuture); + rca.mItemsFuture = CompletableFuture.completedFuture(null); + continue; + } + + // Differentiate between the normal collection actions and the ones with + // intents. + if (rca.mServiceIntent != null) { + String uri = rca.mServiceIntent.toUri(0); + int index = mIdToUriMapping.indexOfValue(uri); + if (index == -1) { + int newIntentId = mIdToUriMapping.size(); + rca.mIntentId = newIntentId; + mIdToUriMapping.put(newIntentId, uri); + // mUriToIntentMapping.put(uri, mServiceIntent); + mTempUriToFutureMapping.put(uri, rca.mItemsFuture); + } else { + rca.mIntentId = mIdToUriMapping.keyAt(index); + } + rca.mItemsFuture = CompletableFuture.completedFuture(null); + } else { + RemoteCollectionItems items = getCollectionItemsFromFuture( + rca.mItemsFuture); + for (RemoteViews views : items.mViews) { + views.collectAllIntents(); + } + } + } else if (action instanceof ViewGroupActionAdd vgaa + && vgaa.mNestedViews != null) { + vgaa.mNestedViews.collectAllIntents(); + } + } + } + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mIdToUriMapping.size()); + for (int i = 0; i < mIdToUriMapping.size(); i++) { + out.writeInt(mIdToUriMapping.keyAt(i)); + String intentUri = mIdToUriMapping.valueAt(i); + out.writeString8(intentUri); + RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null + ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri)) + : mUriToCollectionMapping.get(intentUri); + items.writeToParcel(out, flags, true); + } + } + } + private class SetRemoteViewsAdapterIntent extends Action { Intent mIntent; boolean mIsAsync = false; @@ -3850,9 +4008,12 @@ public class RemoteViews implements Parcelable, Filter { private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) { if (hierarchyRoot == null) { mBitmapCache = src.mBitmapCache; + // We need to create a new instance because we don't reconstruct collection cache + mCollectionCache = new RemoteCollectionCache(src.mCollectionCache); mApplicationInfoCache = src.mApplicationInfoCache; } else { mBitmapCache = hierarchyRoot.mBitmapCache; + mCollectionCache = hierarchyRoot.mCollectionCache; mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache; } if (hierarchyRoot == null || src.mIsRoot) { @@ -3926,6 +4087,7 @@ public class RemoteViews implements Parcelable, Filter { mBitmapCache = new BitmapCache(parcel); // Store the class cookies such that they are available when we clone this RemoteView. mClassCookies = parcel.copyClassCookies(); + mCollectionCache = new RemoteCollectionCache(parcel); } else { configureAsChild(rootData); } @@ -4087,6 +4249,7 @@ public class RemoteViews implements Parcelable, Filter { private void configureAsChild(@NonNull HierarchyRootData rootData) { mIsRoot = false; mBitmapCache = rootData.mBitmapCache; + mCollectionCache = rootData.mRemoteCollectionCache; mApplicationInfoCache = rootData.mApplicationInfoCache; mClassCookies = rootData.mClassCookies; configureDescendantsAsChildren(); @@ -6357,6 +6520,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); + mCollectionCache.writeToParcel(dest, flags); } mApplication.writeToParcel(dest, flags); if (mIsRoot || mIdealSize == null) { @@ -6373,6 +6537,7 @@ public class RemoteViews implements Parcelable, Filter { dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS); if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); + mCollectionCache.writeToParcel(dest, flags); } dest.writeInt(mSizedRemoteViews.size()); for (RemoteViews view : mSizedRemoteViews) { @@ -6384,6 +6549,7 @@ public class RemoteViews implements Parcelable, Filter { // is shared by all children. if (mIsRoot) { mBitmapCache.writeBitmapsToParcel(dest, flags); + mCollectionCache.writeToParcel(dest, flags); } mLandscape.writeToParcel(dest, flags); // Both RemoteViews already share the same package and user @@ -7262,19 +7428,23 @@ public class RemoteViews implements Parcelable, Filter { } private HierarchyRootData getHierarchyRootData() { - return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies); + return new HierarchyRootData(mBitmapCache, mCollectionCache, + mApplicationInfoCache, mClassCookies); } private static final class HierarchyRootData { final BitmapCache mBitmapCache; + final RemoteCollectionCache mRemoteCollectionCache; final ApplicationInfoCache mApplicationInfoCache; final Map<Class, Object> mClassCookies; HierarchyRootData( BitmapCache bitmapCache, + RemoteCollectionCache remoteCollectionCache, ApplicationInfoCache applicationInfoCache, Map<Class, Object> classCookies) { mBitmapCache = bitmapCache; + mRemoteCollectionCache = remoteCollectionCache; mApplicationInfoCache = applicationInfoCache; mClassCookies = classCookies; } diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl index 7b7e34172fed..4706dfd1a76c 100644 --- a/core/java/android/window/ITaskFragmentOrganizerController.aidl +++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl @@ -19,6 +19,7 @@ package android.window; import android.os.IBinder; import android.view.RemoteAnimationDefinition; import android.window.ITaskFragmentOrganizer; +import android.window.RemoteTransition; import android.window.WindowContainerTransaction; /** @hide */ @@ -65,7 +66,10 @@ interface ITaskFragmentOrganizerController { /** * Requests the server to apply the given {@link WindowContainerTransaction}. + * + * {@link RemoteTransition} can only be used by a system organizer and + * {@code shouldApplyIndependently} must be {@code true}. See {@link registerOrganizer}. */ void applyTransaction(in WindowContainerTransaction wct, int transitionType, - boolean shouldApplyIndependently); + boolean shouldApplyIndependently, in RemoteTransition remoteTransition); } diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java index a6c9cecb508f..5c113f865d45 100644 --- a/core/java/android/window/TaskFragmentOrganizer.java +++ b/core/java/android/window/TaskFragmentOrganizer.java @@ -275,7 +275,31 @@ public class TaskFragmentOrganizer extends WindowOrganizer { } wct.setTaskFragmentOrganizer(mInterface); try { - getController().applyTransaction(wct, transitionType, shouldApplyIndependently); + getController().applyTransaction( + wct, transitionType, shouldApplyIndependently, null /* remoteTransition */); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Applies a transaction with a {@link RemoteTransition}. Only a system organizer is allowed to + * use {@link RemoteTransition}. See {@link TaskFragmentOrganizer#registerOrganizer(boolean)}. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG) + public void applySystemTransaction(@NonNull WindowContainerTransaction wct, + @TaskFragmentTransitionType int transitionType, + @Nullable RemoteTransition remoteTransition) { + if (wct.isEmpty()) { + return; + } + wct.setTaskFragmentOrganizer(mInterface); + try { + getController().applyTransaction( + wct, transitionType, remoteTransition != null /* shouldApplyIndependently */, + remoteTransition); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS new file mode 100644 index 000000000000..fa81ee3905c3 --- /dev/null +++ b/core/java/android/window/flags/OWNERS @@ -0,0 +1 @@ +per-file responsible_apis.aconfig = file:/BAL_OWNERS
\ No newline at end of file diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig new file mode 100644 index 000000000000..4bfb17700a76 --- /dev/null +++ b/core/java/android/window/flags/responsible_apis.aconfig @@ -0,0 +1,22 @@ +package: "com.android.window.flags" + +flag { + name: "bal_require_opt_in_by_pending_intent_creator" + namespace: "responsible_apis" + description: "Require the PendingIntent creator to opt in starting with Android 15" + bug: "296478951" +} + +flag { + name: "bal_dont_bring_existing_background_task_stack_to_fg" + namespace: "responsible_apis" + description: "When starting a PendingIntent with ONLY creator privileges, don't bring the existing task stack to foreground" + bug: "296478675" +} + +flag { + name: "bal_show_toasts" + namespace: "responsible_apis" + description: "Enable toasts to indicate (potential) BAL blocking." + bug: "308059069" +}
\ No newline at end of file diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig new file mode 100644 index 000000000000..09be0cfc5fb0 --- /dev/null +++ b/core/java/android/window/flags/wallpaper_manager.aconfig @@ -0,0 +1,8 @@ +package: "com.android.window.flags" + +flag { + name: "always_update_wallpaper_permission" + namespace: "wear_frameworks" + description: "Allow out of focus process to update wallpaper complications" + bug: "271132915" +}
\ No newline at end of file diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index 77e150239803..df6c1538fc6d 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -106,6 +106,10 @@ public class SystemUiSystemPropertiesFlags { /** b/301242692: Visit extra URIs used in notifications to prevent security issues. */ public static final Flag VISIT_RISKY_URIS = devFlag( "persist.sysui.notification.visit_risky_uris"); + + /** b/303716154: For debugging only: use short bitmap duration. */ + public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag( + "persist.sysui.notification.debug_short_bitmap_duration"); } //// == End of flags. Everything below this line is the implementation. == //// diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 0b69030d384f..9d88a2341fad 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -401,6 +401,12 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { mChangeType = PACKAGE_UPDATING; onPackageUpdateStarted(pkg, uid); + if (intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false)) { + // In case it is a removal event due to archiving, we trigger package + // update event to refresh details like icons, title etc. corresponding to + // the archived app. + onPackageModified(pkg); + } } else { mChangeType = PACKAGE_PERMANENT_CHANGE; // We only consider something to have changed if this is diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 1c5f4f0f1369..cab84bb01f70 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -236,6 +236,9 @@ public final class Zygote { /** Bind mount app storage dirs to lower fs not via fuse */ public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs"; + /** Bind the system properties to an alternate set, for appcompat reasons */ + public static final String BIND_MOUNT_SYSPROP_OVERRIDES = "--bind-mount-sysprop-overrides"; + /** * 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 @@ -353,6 +356,8 @@ public final class Zygote { * @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps. * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. + * @param bindMountSyspropOverrides True if the zygote needs to mount the override system + * properties * * @return 0 if this is the child, pid of the child * if this is the parent, or -1 on error. @@ -361,14 +366,15 @@ public final class Zygote { int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList, - boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) { + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs, + boolean bindMountSyspropOverrides) { ZygoteHooks.preFork(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList, allowlistedDataInfoList, bindMountAppDataDirs, - bindMountAppStorageDirs); + bindMountAppStorageDirs, bindMountSyspropOverrides); if (pid == 0) { // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -391,7 +397,7 @@ public final class Zygote { int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList, boolean bindMountAppDataDirs, - boolean bindMountAppStorageDirs); + boolean bindMountAppStorageDirs, boolean bindMountSyspropOverrides); /** * Specialize an unspecialized app process. The current VM must have been started @@ -421,16 +427,19 @@ public final class Zygote { * @param allowlistedDataInfoList Like pkgDataInfoList, but it's for allowlisted apps. * @param bindMountAppDataDirs True if the zygote needs to mount data dirs. * @param bindMountAppStorageDirs True if the zygote needs to mount storage dirs. + * @param bindMountSyspropOverrides True if the zygote needs to mount the override system + * properties */ 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, String[] allowlistedDataInfoList, - boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) { + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs, + boolean bindMountSyspropOverrides) { nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, isTopApp, pkgDataInfoList, allowlistedDataInfoList, - bindMountAppDataDirs, bindMountAppStorageDirs); + bindMountAppDataDirs, bindMountAppStorageDirs, bindMountSyspropOverrides); // Note that this event ends at the end of handleChildProc. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); @@ -455,7 +464,8 @@ public final class Zygote { int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp, String[] pkgDataInfoList, String[] allowlistedDataInfoList, - boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs); + boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs, + boolean bindMountSyspropOverrides); /** * Called to do any initialization before starting an application. @@ -866,7 +876,8 @@ public final class Zygote { args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir, args.mIsTopApp, args.mPkgDataInfoList, args.mAllowlistedDataInfoList, - args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs); + args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs, + args.mBindMountSyspropOverrides); // While `specializeAppProcess` sets the thread name on the process's main thread, this // is distinct from the app process name which appears in stack traces, as the latter is diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index ef8398294c5b..86b9a59fef72 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -243,6 +243,11 @@ class ZygoteArguments { boolean mBindMountAppDataDirs; /** + * @see Zygote#BIND_MOUNT_SYSPROP_OVERRIDES + */ + boolean mBindMountSyspropOverrides; + + /** * Constructs instance and parses args * * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count. @@ -481,6 +486,8 @@ class ZygoteArguments { mBindMountAppStorageDirs = true; } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) { mBindMountAppDataDirs = true; + } else if (arg.equals(Zygote.BIND_MOUNT_SYSPROP_OVERRIDES)) { + mBindMountSyspropOverrides = true; } else { unprocessedArg = arg; break; diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 5fe086da8c6a..cbe070048811 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -257,7 +257,8 @@ class ZygoteConnection { parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList, parsedArgs.mAllowlistedDataInfoList, parsedArgs.mBindMountAppDataDirs, - parsedArgs.mBindMountAppStorageDirs); + parsedArgs.mBindMountAppStorageDirs, + parsedArgs.mBindMountSyspropOverrides); try { if (pid == 0) { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index b12e14782361..9c1bea779201 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -59,6 +59,8 @@ #include <sys/resource.h> #include <sys/socket.h> #include <sys/stat.h> +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include <sys/_system_properties.h> #include <sys/time.h> #include <sys/types.h> #include <sys/un.h> @@ -1691,6 +1693,131 @@ static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn) { fail_fn(CREATE_ERROR("Error dir is not ready %s: %s", dir_path, strerror(errno))); } +// All public String android.os.Build constants, and the system properties they're pulled from +std::pair<const char*, const char*> build_constants[] = { + std::pair("ID", "ro.build.id"), + std::pair("DISPLAY", "ro.build.display.id"), + std::pair("PRODUCT", "ro.product.name"), + std::pair("DEVICE", "ro.product.device"), + std::pair("BOARD", "ro.product.board"), + std::pair("MANUFACTURER", "ro.product.manufacturer"), + std::pair("BRAND", "ro.product.brand"), + std::pair("MODEL", "ro.product.model"), + std::pair("BOOTLOADER", "ro.bootloader"), + std::pair("HARDWARE", "ro.hardware"), + std::pair("SKU", "ro.boot.hardware.sku"), + std::pair("ODM_SKU", "ro.boot.product.hardware.sku"), + std::pair("TAGS", "ro.build.tags"), + std::pair("TYPE", "ro.build.type"), + std::pair("USER", "ro.build.user"), + std::pair("HOST", "ro.build.host"), +}; + +// All public String Build.VERSION constants, and the system properties they're pulled from +std::pair<const char*, const char*> build_version_constants[] = { + std::pair("INCREMENTAL", "ro.build.version.incremental"), + std::pair("RELEASE", "ro.build.version.release"), + std::pair("RELEASE_OR_CODENAME", "ro.build.version.release_or_codename"), + std::pair("RELEASE_OR_PREVIEW_DISPLAY", "ro.build.version.release_or_preview_display"), + std::pair("BASE_OS", "ro.build.version.base_os"), + std::pair("SECURITY_PATCH", "ro.build.version.security_patch"), + std::pair("SDK", "ro.build.version.sdk"), + std::pair("PREVIEW_SDK_FINGERPRINT", "ro.build.version.preview_sdk_fingerprint"), + std::pair("CODENAME", "ro.build.version.codename"), +}; + +static void ReloadBuildJavaConstant(JNIEnv* env, jclass build_class, const char* field_name, + const char* field_signature, const char* sysprop_name) { + const prop_info* prop_info = __system_property_find(sysprop_name); + std::string new_value; + __system_property_read_callback( + prop_info, + [](void* cookie, const char* name, const char* value, unsigned serial) { + auto new_value = reinterpret_cast<std::string*>(cookie); + *new_value = value; + }, + &new_value); + jfieldID fieldId = env->GetStaticFieldID(build_class, field_name, field_signature); + if (strcmp(field_signature, "I") == 0) { + env->SetStaticIntField(build_class, fieldId, jint(strtol(new_value.c_str(), nullptr, 0))); + } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) { + jstring string_val = env->NewStringUTF(new_value.c_str()); + env->SetStaticObjectField(build_class, fieldId, string_val); + } else if (strcmp(field_signature, "[Ljava/lang/String;") == 0) { + auto stream = std::stringstream(new_value); + std::vector<std::string> items; + std::string segment; + while (std::getline(stream, segment, ',')) { + items.push_back(segment); + } + jclass string_class = env->FindClass("java/lang/String"); + jobjectArray string_arr = env->NewObjectArray(items.size(), string_class, nullptr); + for (size_t i = 0; i < items.size(); i++) { + jstring string_arr_val = env->NewStringUTF(items.at(i).c_str()); + env->SetObjectArrayElement(string_arr, i, string_arr_val); + } + env->SetStaticObjectField(build_class, fieldId, string_arr); + } else if (strcmp(field_signature, "J") == 0) { + env->SetStaticLongField(build_class, fieldId, jlong(strtoll(new_value.c_str(), nullptr, 0))); + } +} + +static void ReloadBuildJavaConstants(JNIEnv* env) { + jclass build_cls = env->FindClass("android/os/Build"); + size_t arr_size = sizeof(build_constants) / sizeof(build_constants[0]); + for (int i = 0; i < arr_size; i++) { + const char* field_name = build_constants[i].first; + const char* sysprop_name = build_constants[i].second; + ReloadBuildJavaConstant(env, build_cls, field_name, "Ljava/lang/String;", sysprop_name); + } + jclass build_version_cls = env->FindClass("android/os/Build$VERSION"); + arr_size = sizeof(build_version_constants) / sizeof(build_version_constants[0]); + for (int i = 0; i < arr_size; i++) { + const char* field_name = build_version_constants[i].first; + const char* sysprop_name = build_version_constants[i].second; + ReloadBuildJavaConstant(env, build_version_cls, field_name, "Ljava/lang/String;", sysprop_name); + } + + // Reload the public String[] constants + ReloadBuildJavaConstant(env, build_cls, "SUPPORTED_ABIS", "[Ljava/lang/String;", + "ro.product.cpu.abilist"); + ReloadBuildJavaConstant(env, build_cls, "SUPPORTED_32_BIT_ABIS", "[Ljava/lang/String;", + "ro.product.cpu.abilist32"); + ReloadBuildJavaConstant(env, build_cls, "SUPPORTED_64_BIT_ABIS", "[Ljava/lang/String;", + "ro.product.cpu.abilist64"); + ReloadBuildJavaConstant(env, build_version_cls, "ALL_CODENAMES", "[Ljava/lang/String;", + "ro.build.version.all_codenames"); + + // Reload the public int/long constants + ReloadBuildJavaConstant(env, build_cls, "TIME", "J", "ro.build.date.utc"); + ReloadBuildJavaConstant(env, build_version_cls, "SDK_INT", "I", "ro.build.version.sdk"); + ReloadBuildJavaConstant(env, build_version_cls, "PREVIEW_SDK_INT", "I", + "ro.build.version.preview_sdk"); + + // Re-derive the fingerprint + jmethodID derive_fingerprint = + env->GetStaticMethodID(build_cls, "deriveFingerprint", "()Ljava/lang/String;"); + auto new_fingerprint = (jstring)(env->CallStaticObjectMethod(build_cls, derive_fingerprint)); + jfieldID fieldId = env->GetStaticFieldID(build_cls, "FINGERPRINT", "Ljava/lang/String;"); + env->SetStaticObjectField(build_cls, fieldId, new_fingerprint); +} + +static void BindMountSyspropOverride(fail_fn_t fail_fn, JNIEnv* env) { + std::string source = "/dev/__properties__/appcompat_override"; + std::string target = "/dev/__properties__"; + if (access(source.c_str(), F_OK) != 0) { + fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno))); + } + if (access(target.c_str(), F_OK) != 0) { + fail_fn(CREATE_ERROR("Error accessing %s: %s", target.c_str(), strerror(errno))); + } + BindMount(source, target, fail_fn); + // Reload the system properties file, to ensure new values are read into memory + __system_properties_zygote_reload(); + // android.os.Build constants are pulled from system properties, so they must be reloaded, too + ReloadBuildJavaConstants(env); +} + static void BindMountStorageToLowerFs(const userid_t user_id, const uid_t uid, const char* dir_name, const char* package, fail_fn_t fail_fn) { bool hasSdcardFs = IsSdcardfsUsed(); @@ -1754,7 +1881,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jstring managed_instruction_set, jstring managed_app_data_dir, bool is_top_app, jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list, bool mount_data_dirs, - bool mount_storage_dirs) { + bool mount_storage_dirs, bool mount_sysprop_overrides) { 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); @@ -1807,6 +1934,10 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, fail_fn); } + if (mount_sysprop_overrides) { + BindMountSyspropOverride(fail_fn, env); + } + // If this zygote isn't root, it won't be able to create a process group, // since the directory is owned by root. if (!is_system_server && getuid() == 0) { @@ -2360,7 +2491,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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, jobjectArray allowlisted_data_info_list, - jboolean mount_data_dirs, jboolean mount_storage_dirs) { + jboolean mount_data_dirs, jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); if (UNLIKELY(managed_fds_to_close == nullptr)) { @@ -2403,7 +2534,7 @@ 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, allowlisted_data_info_list, mount_data_dirs == JNI_TRUE, - mount_storage_dirs == JNI_TRUE); + mount_storage_dirs == JNI_TRUE, mount_sysprop_overrides == JNI_TRUE); } return pid; } @@ -2439,7 +2570,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true, false, nullptr, nullptr, /* is_top_app= */ false, /* pkg_data_info_list */ nullptr, - /* allowlisted_data_info_list */ nullptr, false, false); + /* allowlisted_data_info_list */ nullptr, false, 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); @@ -2591,14 +2722,14 @@ static void com_android_internal_os_Zygote_nativeSpecializeAppProcess( jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app, jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list, jboolean mount_data_dirs, - jboolean mount_storage_dirs) { + jboolean mount_storage_dirs, jboolean mount_sysprop_overrides) { 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, allowlisted_data_info_list, mount_data_dirs == JNI_TRUE, - mount_storage_dirs == JNI_TRUE); + mount_storage_dirs == JNI_TRUE, mount_sysprop_overrides == JNI_TRUE); } /** @@ -2876,7 +3007,7 @@ static void com_android_internal_os_Zygote_nativeAllowFilesOpenedByPreload(JNIEn static const JNINativeMethod gMethods[] = { {"nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/" - "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I", + "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)I", (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize}, {"nativeForkSystemServer", "(II[II[[IJJ)I", (void*)com_android_internal_os_Zygote_nativeForkSystemServer}, @@ -2892,7 +3023,7 @@ static const JNINativeMethod gMethods[] = { (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry}, {"nativeSpecializeAppProcess", "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/" - "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V", + "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZZ)V", (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess}, {"nativeInitNativeState", "(Z)V", (void*)com_android_internal_os_Zygote_nativeInitNativeState}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8c91be8b21c0..c75f99616f5b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4065,6 +4065,7 @@ <!-- Allow apps to always update wallpaper by sending data. @SystemApi @hide + @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission") --> <permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER" android:protectionLevel="internal|role" /> diff --git a/core/res/res/drawable/archived_app_cloud_overlay.xml b/core/res/res/drawable/archived_app_cloud_overlay.xml new file mode 100644 index 000000000000..611e0f39bb5c --- /dev/null +++ b/core/res/res/drawable/archived_app_cloud_overlay.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="20dp" + android:height="20dp" + android:viewportWidth="60" + android:viewportHeight="60"> + <group + android:scaleX="1.2" + android:scaleY="1.2" + android:translateX="15" + android:translateY="14"> + <path + android:fillColor="@android:color/white" + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18L6,18c-2.21,0 -4,-1.79 -4,-4 0,-2.05 1.53,-3.76 3.56,-3.97l1.07,-0.11 0.5,-0.95C8.08,7.14 9.94,6 12,6c2.62,0 4.88,1.86 5.39,4.43l0.3,1.5 1.53,0.11c1.56,0.1 2.78,1.41 2.78,2.96 0,1.65 -1.35,3 -3,3zM13.45,10h-2.9v3L8,13l4,4 4,-4h-2.55z"/> + </group> +</vector>
\ No newline at end of file diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 14bbb966f750..1aa1fea95049 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1405,6 +1405,7 @@ <java-symbol type="drawable" name="ic_test_badge_no_background" /> <java-symbol type="drawable" name="ic_test_icon_badge_experiment" /> <java-symbol type="drawable" name="ic_instant_icon_badge_bolt" /> + <java-symbol type="drawable" name="archived_app_cloud_overlay" /> <java-symbol type="drawable" name="emulator_circular_window_overlay" /> <java-symbol type="drawable" name="ic_qs_battery_saver" /> <java-symbol type="drawable" name="ic_qs_bluetooth" /> diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java index 21d1dbb04519..5d213caf61e6 100644 --- a/core/tests/coretests/src/android/os/PowerManagerTest.java +++ b/core/tests/coretests/src/android/os/PowerManagerTest.java @@ -19,11 +19,17 @@ package android.os; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; +import android.os.Flags; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.test.AndroidTestCase; import androidx.test.InstrumentationRegistry; @@ -31,6 +37,7 @@ import androidx.test.filters.SmallTest; import androidx.test.uiautomator.UiDevice; import org.junit.After; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -64,6 +71,10 @@ public class PowerManagerTest extends AndroidTestCase { System.loadLibrary("powermanagertest_jni"); } + // Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect. + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + /** * Setup any common data for the upcoming tests. */ @@ -454,4 +465,27 @@ public class PowerManagerTest extends AndroidTestCase { parcelBatterySaverPolicyConfigToNativeAndVerify(bs2); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API) + public void testBatterySaverSupported_isSupported() throws RemoteException { + IPowerManager powerManager = mock(IPowerManager.class); + PowerManager pm = new PowerManager(mContext, powerManager, + mock(IThermalService.class), + Handler.createAsync(Looper.getMainLooper())); + when(powerManager.isBatterySaverSupported()).thenReturn(true); + + assertTrue(pm.isBatterySaverSupported()); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API) + public void testBatterySaverSupported_isNotSupported() throws RemoteException { + IPowerManager powerManager = mock(IPowerManager.class); + PowerManager pm = new PowerManager(mContext, powerManager, + mock(IThermalService.class), + Handler.createAsync(Looper.getMainLooper())); + when(powerManager.isBatterySaverSupported()).thenReturn(false); + + assertFalse(pm.isBatterySaverSupported()); + } } diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index 5c411d5335a7..35ddfdb3723b 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -23,6 +23,7 @@ import static org.testng.Assert.assertThrows; import android.content.ContentCaptureOptions; import android.content.Context; +import android.view.WindowManager; import com.android.internal.util.RingBuffer; @@ -147,6 +148,52 @@ public class ContentCaptureManagerTest { assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); } + @Test + public void testUpdateWindowAttribute_setFlagSecure() { + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS); + // Ensure main session is created. + final MainContentCaptureSession unused = manager.getMainContentCaptureSession(); + final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams(); + initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE; + + manager.updateWindowAttributes(initialParam); + + assertThat(manager.isContentCaptureEnabled()).isFalse(); + } + + @Test + public void testUpdateWindowAttribute_clearFlagSecure() { + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS); + // Ensure main session is created. + final MainContentCaptureSession unused = manager.getMainContentCaptureSession(); + final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams(); + initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE; + // Default param does not have FLAG_SECURE set. + final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams(); + + manager.updateWindowAttributes(initialParam); + manager.updateWindowAttributes(resetParam); + + assertThat(manager.isContentCaptureEnabled()).isTrue(); + } + + @Test + public void testUpdateWindowAttribute_clearFlagSecureAfterDisabledByApp() { + final ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS); + // Ensure main session is created. + final MainContentCaptureSession unused = manager.getMainContentCaptureSession(); + // Default param does not have FLAG_SECURE set. + final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams(); + + manager.setContentCaptureEnabled(false); + manager.updateWindowAttributes(resetParam); + + assertThat(manager.isContentCaptureEnabled()).isFalse(); + } + private ContentCaptureOptions createOptions( ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) { return new ContentCaptureOptions( diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java index a3399070ebcd..8e653f5e828f 100644 --- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java +++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java @@ -298,6 +298,43 @@ public class PackageMonitorTest { } @Test + public void testPackageMonitorDoHandlePackageEventPackageRemovedReplacingArchived() { + PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor()); + + Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED); + intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null)); + intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID); + intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID); + intent.putExtra(Intent.EXTRA_REPLACING, true); + intent.putExtra(Intent.EXTRA_ARCHIVAL, true); + intent.putExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, true); + spyPackageMonitor.doHandlePackageEvent(intent); + + verify(spyPackageMonitor, times(1)).onBeginPackageChanges(); + verify(spyPackageMonitor, times(1)) + .onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID)); + verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME)); + + ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(spyPackageMonitor, times(1)) + .onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME), argumentCaptor.capture()); + Bundle capturedExtras = argumentCaptor.getValue(); + Bundle expectedExtras = intent.getExtras(); + assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE)); + assertThat(capturedExtras.getInt(Intent.EXTRA_UID)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING)); + assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS)) + .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS)); + + verify(spyPackageMonitor, times(1)) + .onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING)); + verify(spyPackageMonitor, times(1)).onFinishPackageChanges(); + } + + @Test public void testPackageMonitorDoHandlePackageEventPackageRemovedNotReplacing() throws Exception { PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor()); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 87be13ab4a93..dc2b0561957d 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2653,6 +2653,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/TransitionController.java" }, + "261227010": { + "message": "Content Recording: Unable to tell log windowing mode change: %s", + "level": "ERROR", + "group": "WM_DEBUG_CONTENT_RECORDING", + "at": "com\/android\/server\/wm\/ContentRecorder.java" + }, "269576220": { "message": "Resuming rotation after drag", "level": "DEBUG", diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 15d26ebe66f6..27325694073c 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -272,6 +272,17 @@ public final class RenderNode { void positionChanged(long frameNumber, int left, int top, int right, int bottom); /** + * Called by native by a Rendering Worker thread to update window position; includes + * the local rect that represents the clipped area of the RenderNode's bounds. + * + * @hide + */ + default void positionChanged(long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + positionChanged(frameNumber, left, top, right, bottom); + } + + /** * Called by JNI * * @hide */ @@ -287,6 +298,23 @@ public final class RenderNode { } /** + * Called by JNI + * + * @hide */ + static boolean callPositionChanged2(WeakReference<PositionUpdateListener> weakListener, + long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + final PositionUpdateListener listener = weakListener.get(); + if (listener != null) { + listener.positionChanged(frameNumber, left, top, right, bottom, clipLeft, + clipTop, clipRight, clipBottom); + return true; + } else { + return false; + } + } + + /** * Call to apply a stretch effect to any child SurfaceControl layers * * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls? @@ -371,6 +399,15 @@ public final class RenderNode { } @Override + public void positionChanged(long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + for (PositionUpdateListener pul : mListeners) { + pul.positionChanged(frameNumber, left, top, right, bottom, clipLeft, clipTop, + clipRight, clipBottom); + } + } + + @Override public void positionLost(long frameNumber) { for (PositionUpdateListener pul : mListeners) { pul.positionLost(frameNumber); diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml index 10c9562cf651..d8ae9c8c64a6 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml @@ -26,7 +26,8 @@ android:id="@+id/bubble_manage_menu_dismiss_container" android:background="@drawable/bubble_manage_menu_row" android:layout_width="match_parent" - android:layout_height="@dimen/bubble_menu_item_height" + android:layout_height="wrap_content" + android:minHeight="@dimen/bubble_menu_item_height" android:gravity="center_vertical" android:paddingStart="@dimen/bubble_menu_padding" android:paddingEnd="@dimen/bubble_menu_padding" @@ -52,7 +53,8 @@ android:id="@+id/bubble_manage_menu_dont_bubble_container" android:background="@drawable/bubble_manage_menu_row" android:layout_width="match_parent" - android:layout_height="@dimen/bubble_menu_item_height" + android:layout_height="wrap_content" + android:minHeight="@dimen/bubble_menu_item_height" android:gravity="center_vertical" android:paddingStart="@dimen/bubble_menu_padding" android:paddingEnd="@dimen/bubble_menu_padding" @@ -78,7 +80,8 @@ android:id="@+id/bubble_manage_menu_settings_container" android:background="@drawable/bubble_manage_menu_row" android:layout_width="match_parent" - android:layout_height="@dimen/bubble_menu_item_height" + android:layout_height="wrap_content" + android:minHeight="@dimen/bubble_menu_item_height" android:gravity="center_vertical" android:paddingStart="@dimen/bubble_menu_padding" android:paddingEnd="@dimen/bubble_menu_padding" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index 4d87c9583f64..ac75c73d7e6d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -255,8 +255,13 @@ class ActivityEmbeddingAnimationRunner { int offsetLayer = TYPE_LAYER_OFFSET; final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>(); for (TransitionInfo.Change change : openingChanges) { + final Animation animation = + animationProvider.get(info, change, openingWholeScreenBounds); + if (animation.getDuration() == 0) { + continue; + } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( - info, change, animationProvider, openingWholeScreenBounds); + info, change, animation, openingWholeScreenBounds); if (isOpening) { adapter.overrideLayer(offsetLayer++); } @@ -275,8 +280,13 @@ class ActivityEmbeddingAnimationRunner { adapters.add(snapshotAdapter); } } + final Animation animation = + animationProvider.get(info, change, closingWholeScreenBounds); + if (animation.getDuration() == 0) { + continue; + } final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( - info, change, animationProvider, closingWholeScreenBounds); + info, change, animation, closingWholeScreenBounds); if (!isOpening) { adapter.overrideLayer(offsetLayer++); } @@ -353,8 +363,7 @@ class ActivityEmbeddingAnimationRunner { @NonNull private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter( @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, - @NonNull AnimationProvider animationProvider, @NonNull Rect wholeAnimationBounds) { - final Animation animation = animationProvider.get(info, change, wholeAnimationBounds); + @NonNull Animation animation, @NonNull Rect wholeAnimationBounds) { return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(), wholeAnimationBounds, TransitionUtil.getRootFor(change, info)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index cc9c2beff873..6cd1324c7d24 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -22,6 +22,8 @@ import static android.app.ActivityOptions.ANIM_CUSTOM; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE; import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.view.animation.AlphaAnimation; @@ -34,8 +36,6 @@ import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import android.window.TransitionInfo; -import androidx.annotation.NonNull; - import com.android.internal.policy.TransitionAnimation; import com.android.wm.shell.util.TransitionUtil; @@ -201,11 +201,10 @@ class ActivityEmbeddingAnimationSpec { Animation loadOpenAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); - final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + final Animation customAnimation = loadCustomAnimation(info, isEnter); final Animation animation; - if (options != null && options.getType() == ANIM_CUSTOM) { - animation = mTransitionAnimation.loadAnimationRes(options.getPackageName(), - isEnter ? options.getEnterResId() : options.getExitResId()); + if (customAnimation != null) { + animation = customAnimation; } else if (shouldShowBackdrop(info, change)) { animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter ? com.android.internal.R.anim.task_fragment_clear_top_open_enter @@ -229,11 +228,10 @@ class ActivityEmbeddingAnimationSpec { Animation loadCloseAnimation(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = TransitionUtil.isOpeningType(change.getMode()); - final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + final Animation customAnimation = loadCustomAnimation(info, isEnter); final Animation animation; - if (options != null && options.getType() == ANIM_CUSTOM) { - animation = mTransitionAnimation.loadAnimationRes(options.getPackageName(), - isEnter ? options.getEnterResId() : options.getExitResId()); + if (customAnimation != null) { + animation = customAnimation; } else if (shouldShowBackdrop(info, change)) { animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter ? com.android.internal.R.anim.task_fragment_clear_top_close_enter @@ -259,4 +257,21 @@ class ActivityEmbeddingAnimationSpec { mTransitionAnimation, false); return a != null && a.getShowBackdrop(); } + + @Nullable + private Animation loadCustomAnimation(@NonNull TransitionInfo info, boolean isEnter) { + final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); + if (options == null || options.getType() != ANIM_CUSTOM) { + return null; + } + final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(), + isEnter ? options.getEnterResId() : options.getExitResId()); + if (anim != null) { + return anim; + } + // The app may be intentional to use an invalid resource as a no-op animation. + // ActivityEmbeddingAnimationRunner#createOpenCloseAnimationAdapters will skip the + // animation with duration 0. Then it will use prepareForJumpCut for empty adapters. + return new AlphaAnimation(1f, 1f); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java index 24479d7b5f39..a596cef9562a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java @@ -353,7 +353,6 @@ public class CrossActivityAnimation extends ShellBackAnimation { closingLeft += mapRange(interpolatedProgress, deltaXMin, deltaXMax); // Move the window along the Y axis. - final float deltaYRatio = (touchY - mInitialTouchPos.y) / height; final float closingTop = (height - closingHeight) * 0.5f; targetRect.set( closingLeft, closingTop, closingLeft + closingWidth, closingTop + closingHeight); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 9cd318f27355..723a4a7ca664 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -40,7 +40,6 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECI import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_RELAUNCH; -import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL; import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; @@ -422,11 +421,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { continue; } - // The back gesture has animated this change before transition happen, so here we don't - // play the animation again. - if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { - continue; - } // Don't animate anything that isn't independent. if (!TransitionInfo.isIndependent(change, info)) continue; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index b1fc16ddf19b..030f601a7b60 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -44,7 +44,7 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { private IBinder mTransition = null; /** The remote to delegate animation to */ - private final RemoteTransition mRemote; + private RemoteTransition mRemote; public OneShotRemoteHandler(@NonNull ShellExecutor mainExecutor, @NonNull RemoteTransition remote) { @@ -83,6 +83,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { mMainExecutor.execute(() -> { finishCallback.onTransitionFinished(wct); }); + Log.d("b/302551868", "OneShotRemoteHandler#start remote anim null"); + mRemote = null; } }; Transitions.setRunningRemoteTransitionDelegate(mRemote.getAppThread()); @@ -105,6 +107,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */); } finishCallback.onTransitionFinished(null /* wct */); + Log.d("b/302551868", "OneShotRemoteHandler#exception remote anim null"); + mRemote = null; } return true; } @@ -123,6 +127,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { // so just assume the worst-case and clear the local transaction. t.clear(); mMainExecutor.execute(() -> finishCallback.onTransitionFinished(wct)); + Log.d("b/302551868", "OneShotRemoteHandler#merge remote anim null"); + mRemote = null; } }; try { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index baa9acaafa4b..c34a14e5b82b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_SLEEP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.fixScale; +import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; @@ -743,6 +744,11 @@ public class Transitions implements RemoteCallable<Transitions>, if (!change.hasFlags(FLAG_IS_OCCLUDED)) { allOccluded = false; } + // The change has already animated by back gesture, don't need to play transition + // animation on it. + if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) { + info.getChanges().remove(i); + } } // There does not need animation when: // A. Transfer starting window. Apply transfer starting window directly if there is no other diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index bd2eb5b4a8ae..366f7b1e678f 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -28,93 +28,6 @@ filegroup { srcs: ["src/com/android/wm/shell/flicker/utils/*.kt"], } -filegroup { - name: "WMShellFlickerTestsBase-src", - srcs: ["src/com/android/wm/shell/flicker/*.kt"], -} - -filegroup { - name: "WMShellFlickerTestsBubbles-src", - srcs: ["src/com/android/wm/shell/flicker/bubble/*.kt"], -} - -filegroup { - name: "WMShellFlickerTestsPip1-src", - srcs: [ - "src/com/android/wm/shell/flicker/pip/A*.kt", - "src/com/android/wm/shell/flicker/pip/B*.kt", - "src/com/android/wm/shell/flicker/pip/C*.kt", - "src/com/android/wm/shell/flicker/pip/D*.kt", - "src/com/android/wm/shell/flicker/pip/S*.kt", - ], -} - -filegroup { - name: "WMShellFlickerTestsPip2-src", - srcs: [ - "src/com/android/wm/shell/flicker/pip/E*.kt", - ], -} - -filegroup { - name: "WMShellFlickerTestsPip3-src", - srcs: ["src/com/android/wm/shell/flicker/pip/*.kt"], -} - -filegroup { - name: "WMShellFlickerTestsPipCommon-src", - srcs: ["src/com/android/wm/shell/flicker/pip/common/*.kt"], -} - -filegroup { - name: "WMShellFlickerTestsPipApps-src", - srcs: ["src/com/android/wm/shell/flicker/pip/apps/*.kt"], -} - -filegroup { - name: "WMShellFlickerTestsSplitScreenBase-src", - srcs: [ - "src/com/android/wm/shell/flicker/splitscreen/benchmark/*.kt", - ], -} - -filegroup { - name: "WMShellFlickerTestsSplitScreenGroup1-src", - srcs: [ - "src/com/android/wm/shell/flicker/splitscreen/A*.kt", - "src/com/android/wm/shell/flicker/splitscreen/B*.kt", - "src/com/android/wm/shell/flicker/splitscreen/C*.kt", - "src/com/android/wm/shell/flicker/splitscreen/D*.kt", - "src/com/android/wm/shell/flicker/splitscreen/E*.kt", - ], -} - -filegroup { - name: "WMShellFlickerTestsSplitScreenGroup2-src", - srcs: [ - "src/com/android/wm/shell/flicker/splitscreen/*.kt", - ], -} - -filegroup { - name: "WMShellFlickerServicePlatinumTests-src", - srcs: [ - "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt", - "src/com/android/wm/shell/flicker/service/*/scenarios/**/*.kt", - "src/com/android/wm/shell/flicker/service/common/**/*.kt", - ], -} - -filegroup { - name: "WMShellFlickerServiceTests-src", - srcs: [ - "src/com/android/wm/shell/flicker/service/**/*.kt", - ], - exclude_srcs: [ - "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt", - ], -} - java_library { name: "wm-shell-flicker-utils", platform_apis: true, @@ -138,23 +51,8 @@ java_library { ], } -java_library { - name: "wm-shell-flicker-platinum-tests", - platform_apis: true, - optimize: { - enabled: false, - }, - srcs: [ - ":WMShellFlickerServicePlatinumTests-src", - ], - static_libs: [ - "wm-shell-flicker-utils", - ], -} - java_defaults { name: "WMShellFlickerTestsDefaultWithoutTemplate", - manifest: "manifests/AndroidManifest.xml", platform_apis: true, certificate: "platform", optimize: { @@ -187,170 +85,8 @@ java_defaults { test_config_template: "AndroidTestTemplate.xml", } -android_test { - name: "WMShellFlickerTestsOther", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestOther.xml"], - package_name: "com.android.wm.shell.flicker", - instrumentation_target_package: "com.android.wm.shell.flicker", - srcs: [ - "src/**/*.java", - "src/**/*.kt", - ], - exclude_srcs: [ - ":WMShellFlickerTestsBubbles-src", - ":WMShellFlickerTestsPip1-src", - ":WMShellFlickerTestsPip2-src", - ":WMShellFlickerTestsPip3-src", - ":WMShellFlickerTestsPipCommon-src", - ":WMShellFlickerTestsPipApps-src", - ":WMShellFlickerTestsSplitScreenGroup1-src", - ":WMShellFlickerTestsSplitScreenGroup2-src", - ":WMShellFlickerTestsSplitScreenBase-src", - ":WMShellFlickerServiceTests-src", - ":WMShellFlickerServicePlatinumTests-src", - ], -} - -android_test { - name: "WMShellFlickerTestsBubbles", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestBubbles.xml"], - package_name: "com.android.wm.shell.flicker.bubbles", - instrumentation_target_package: "com.android.wm.shell.flicker.bubbles", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsBubbles-src", - ], -} - -android_test { - name: "WMShellFlickerTestsPip1", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestPip.xml"], - package_name: "com.android.wm.shell.flicker.pip", - instrumentation_target_package: "com.android.wm.shell.flicker.pip", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsPip1-src", - ":WMShellFlickerTestsPipCommon-src", - ], -} - -android_test { - name: "WMShellFlickerTestsPip2", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestPip.xml"], - package_name: "com.android.wm.shell.flicker.pip", - instrumentation_target_package: "com.android.wm.shell.flicker.pip", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsPip2-src", - ":WMShellFlickerTestsPipCommon-src", - ], -} - -android_test { - name: "WMShellFlickerTestsPip3", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestPip.xml"], - package_name: "com.android.wm.shell.flicker.pip", - instrumentation_target_package: "com.android.wm.shell.flicker.pip", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsPip3-src", - ":WMShellFlickerTestsPipCommon-src", - ], - exclude_srcs: [ - ":WMShellFlickerTestsPip1-src", - ":WMShellFlickerTestsPip2-src", - ], -} - -android_test { - name: "WMShellFlickerTestsPipApps", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestPip.xml"], - package_name: "com.android.wm.shell.flicker.pip.apps", - instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsPipApps-src", - ":WMShellFlickerTestsPipCommon-src", - ], -} - -android_test { - name: "WMShellFlickerTestsPipAppsCSuite", - defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"], - additional_manifests: ["manifests/AndroidManifestPip.xml"], - package_name: "com.android.wm.shell.flicker.pip.apps", - instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsPipApps-src", - ":WMShellFlickerTestsPipCommon-src", - ], - test_suites: [ - "device-tests", - "csuite", - ], -} - -android_test { - name: "WMShellFlickerTestsSplitScreenGroup1", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"], - package_name: "com.android.wm.shell.flicker.splitscreen", - instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsSplitScreenBase-src", - ":WMShellFlickerTestsSplitScreenGroup1-src", - ], -} - -android_test { - name: "WMShellFlickerTestsSplitScreenGroup2", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"], - package_name: "com.android.wm.shell.flicker.splitscreen", - instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerTestsSplitScreenBase-src", - ":WMShellFlickerTestsSplitScreenGroup2-src", - ], - exclude_srcs: [ - ":WMShellFlickerTestsSplitScreenGroup1-src", - ], -} - -android_test { - name: "WMShellFlickerServiceTests", - defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestService.xml"], - package_name: "com.android.wm.shell.flicker.service", - instrumentation_target_package: "com.android.wm.shell.flicker.service", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerServiceTests-src", - ], -} - -android_test { - name: "WMShellFlickerServicePlatinumTests", +java_library { + name: "WMShellFlickerTestsBase", defaults: ["WMShellFlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestService.xml"], - package_name: "com.android.wm.shell.flicker.service", - instrumentation_target_package: "com.android.wm.shell.flicker.service", - srcs: [ - ":WMShellFlickerTestsBase-src", - ":WMShellFlickerServicePlatinumTests-src", - ], -} - -csuite_test { - name: "csuite-1p3p-pip-flickers", - test_config_template: "csuiteDefaultTemplate.xml", + srcs: ["src/com/android/wm/shell/flicker/*.kt"], } diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp new file mode 100644 index 000000000000..bae701f2cbeb --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp @@ -0,0 +1,41 @@ +// +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "WMShellFlickerTestsAppCompat-src", + srcs: [ + "src/**/*.kt", + ], +} + +android_test { + name: "WMShellFlickerTestsOther", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker", + instrumentation_target_package: "com.android.wm.shell.flicker", + srcs: [":WMShellFlickerTestsAppCompat-src"], + static_libs: ["WMShellFlickerTestsBase"], +} diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml index ae130b8f6f7d..2af1e74fb3d5 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml @@ -1,18 +1,18 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" @@ -69,4 +69,9 @@ android:authorities="${applicationId}.androidx-startup" tools:node="remove" /> </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker" + android:label="WindowManager Flicker Tests"> + </instrumentation> </manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml new file mode 100644 index 000000000000..1df11369a049 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/res/xml/network_security_config.xml index 4bd9ca049f55..4bd9ca049f55 100644 --- a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/res/xml/network_security_config.xml diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt index adf92d8854ff..adf92d8854ff 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt index 744e8c2eb06f..744e8c2eb06f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt index 1e5e42fb077e..1e5e42fb077e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt index 2fa1ec386781..2fa1ec386781 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt index b74aa1d7bf73..b74aa1d7bf73 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt index 68fa8d2fc2e8..68fa8d2fc2e8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt index fcb6931af9a2..fcb6931af9a2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt index ba2b3e7e2781..446aad8a8936 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.appcompat import android.os.Build -import android.tools.common.datatypes.Rect import android.platform.test.annotations.Postsubmit import android.system.helpers.CommandsHelper import android.tools.common.NavBar import android.tools.common.Rotation +import android.tools.common.datatypes.Rect import android.tools.common.flicker.assertions.FlickerTest import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory @@ -65,10 +65,12 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { - private val immersiveApp = LetterboxAppHelper(instrumentation, + private val immersiveApp = + LetterboxAppHelper( + instrumentation, launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL, - component = - ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent()) + component = ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent() + ) private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation) private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) } @@ -84,8 +86,8 @@ class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCo setStartRotation() immersiveApp.launchViaIntent(wmHelper) startDisplayBounds = - wmHelper.currentState.layerState.physicalDisplayBounds - ?: error("Display not found") + wmHelper.currentState.layerState.physicalDisplayBounds + ?: error("Display not found") } transitions { if (isCuttlefishDevice) { @@ -96,12 +98,10 @@ class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCo val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion") uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT) uiDevice.findObject(rotationButtonSelector) - ?: error("rotation button not found") + ?: error("rotation button not found") } } - teardown { - immersiveApp.exit(wmHelper) - } + teardown { immersiveApp.exit(wmHelper) } } @Before @@ -112,48 +112,40 @@ class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCo /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun taskBarLayerIsVisibleAtStartAndEnd() { - } + override fun taskBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun navBarLayerIsVisibleAtStartAndEnd() { - } + override fun navBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun statusBarLayerIsVisibleAtStartAndEnd() { - } + override fun statusBarLayerIsVisibleAtStartAndEnd() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun taskBarWindowIsAlwaysVisible() { - } + override fun taskBarWindowIsAlwaysVisible() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun navBarWindowIsAlwaysVisible() { - } + override fun navBarWindowIsAlwaysVisible() {} /** {@inheritDoc} */ @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun statusBarWindowIsAlwaysVisible() { - } + override fun statusBarWindowIsAlwaysVisible() {} @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun statusBarLayerPositionAtStartAndEnd() { - } + override fun statusBarLayerPositionAtStartAndEnd() {} @Test @Ignore("Not applicable to this CUJ. App is in immersive mode.") - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - } + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {} /** Test that app is fullscreen by checking status bar and task bar visibility. */ @Postsubmit @@ -161,8 +153,9 @@ class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCo fun appWindowFullScreen() { flicker.assertWmEnd { this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) - .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR) - .visibleRegion(immersiveApp).coversExactly(startDisplayBounds) + .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR) + .visibleRegion(immersiveApp) + .coversExactly(startDisplayBounds) } } @@ -170,9 +163,7 @@ class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCo @Postsubmit @Test fun appInOriginalRotation() { - flicker.assertWmEnd { - this.hasRotation(Rotation.ROTATION_90) - } + flicker.assertWmEnd { this.hasRotation(Rotation.ROTATION_90) } } companion object { @@ -189,10 +180,10 @@ class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCo @JvmStatic fun getParams(): Collection<FlickerTest> { return LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_90), - // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) - + supportedRotations = listOf(Rotation.ROTATION_90), + // TODO(b/292403378): 3 button mode not added as rotation button is hidden in + // taskbar + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt index 9792c859cced..9792c859cced 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto new file mode 100644 index 000000000000..406ada97a07d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto @@ -0,0 +1,75 @@ +# Copyright (C) 2023 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.android.wm.shell.flicker" + atrace_apps: "com.android.wm.shell.flicker.other" + atrace_apps: "com.android.wm.shell.flicker.bubbles" + atrace_apps: "com.android.wm.shell.flicker.pip" + atrace_apps: "com.android.wm.shell.flicker.splitscreen" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp new file mode 100644 index 000000000000..c4e9a8479563 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp @@ -0,0 +1,34 @@ +// +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "WMShellFlickerTestsBubbles", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.bubbles", + instrumentation_target_package: "com.android.wm.shell.flicker.bubbles", + srcs: ["src/**/*.kt"], + static_libs: ["WMShellFlickerTestsBase"], +} diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml new file mode 100644 index 000000000000..e6e6f1b21bc3 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml @@ -0,0 +1,77 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wm.shell.flicker.bubble"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Control test app's media session --> + <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- Enable bubble notification--> + <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + + <service android:name=".NotificationListener" + android:exported="true" + android:label="WMShellTestsNotificationListenerService" + android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> + <intent-filter> + <action android:name="android.service.notification.NotificationListenerService" /> + </intent-filter> + </service> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.bubble" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml new file mode 100644 index 000000000000..1df11369a049 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/bubble/OWNERS index 566acc87e42d..566acc87e42d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS +++ b/libs/WindowManager/Shell/tests/flicker/bubble/OWNERS diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml b/libs/WindowManager/Shell/tests/flicker/bubble/res/xml/network_security_config.xml index cf642f63a41d..4bd9ca049f55 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml +++ b/libs/WindowManager/Shell/tests/flicker/bubble/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.wm.shell.flicker"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.wm.shell.flicker" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt index bc095bbacc5a..0c36e29a8315 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt @@ -58,8 +58,9 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) return { setup { MultiWindowUtils.executeShellCommand( - instrumentation, - "settings put secure force_hide_bubbles_user_education 1") + instrumentation, + "settings put secure force_hide_bubbles_user_education 1" + ) notifyManager.setBubblesAllowed( testApp.packageName, uid, @@ -72,8 +73,9 @@ abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) teardown { MultiWindowUtils.executeShellCommand( - instrumentation, - "settings put secure force_hide_bubbles_user_education 0") + instrumentation, + "settings put secure force_hide_bubbles_user_education 0" + ) notifyManager.setBubblesAllowed( testApp.packageName, uid, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt index 55039f59190b..55039f59190b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt index 9ca7bf113589..9ca7bf113589 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt index b007e6b3535c..b007e6b3535c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt index 4959672d865b..4959672d865b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt index 0d95574aca06..0d95574aca06 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto new file mode 100644 index 000000000000..406ada97a07d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto @@ -0,0 +1,75 @@ +# Copyright (C) 2023 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.android.wm.shell.flicker" + atrace_apps: "com.android.wm.shell.flicker.other" + atrace_apps: "com.android.wm.shell.flicker.bubbles" + atrace_apps: "com.android.wm.shell.flicker.pip" + atrace_apps: "com.android.wm.shell.flicker.splitscreen" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml deleted file mode 100644 index fa42a4570b7d..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml +++ /dev/null @@ -1,27 +0,0 @@ -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.wm.shell.flicker.pip"> - - <!-- Enable mocking GPS location --> - <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.wm.shell.flicker.pip" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp new file mode 100644 index 000000000000..386983ce6aae --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp @@ -0,0 +1,136 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "WMShellFlickerTestsPip1-src", + srcs: [ + "src/**/A*.kt", + "src/**/B*.kt", + "src/**/C*.kt", + "src/**/D*.kt", + "src/**/S*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsPip2-src", + srcs: [ + "src/**/E*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsPip3-src", + srcs: ["src/**/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsPipCommon-src", + srcs: ["src/**/common/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsPipApps-src", + srcs: ["src/**/apps/*.kt"], +} + +android_test { + name: "WMShellFlickerTestsPip1", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsPip1-src", + ":WMShellFlickerTestsPipCommon-src", + ], + static_libs: ["WMShellFlickerTestsBase"], +} + +android_test { + name: "WMShellFlickerTestsPip2", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsPip2-src", + ":WMShellFlickerTestsPipCommon-src", + ], + static_libs: ["WMShellFlickerTestsBase"], +} + +android_test { + name: "WMShellFlickerTestsPip3", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsPip3-src", + ":WMShellFlickerTestsPipCommon-src", + ], + exclude_srcs: [ + ":WMShellFlickerTestsPip1-src", + ":WMShellFlickerTestsPip2-src", + ":WMShellFlickerTestsPipApps-src", + ], + static_libs: ["WMShellFlickerTestsBase"], +} + +android_test { + name: "WMShellFlickerTestsPipApps", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.pip.apps", + instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps", + srcs: [ + ":WMShellFlickerTestsPipApps-src", + ":WMShellFlickerTestsPipCommon-src", + ], + static_libs: ["WMShellFlickerTestsBase"], +} + +android_test { + name: "WMShellFlickerTestsPipAppsCSuite", + defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"], + additional_manifests: ["AndroidManifest.xml"], + package_name: "com.android.wm.shell.flicker.pip.apps", + instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps", + srcs: [ + ":WMShellFlickerTestsPipApps-src", + ":WMShellFlickerTestsPipCommon-src", + ], + static_libs: ["WMShellFlickerTestsBase"], + test_suites: [ + "device-tests", + "csuite", + ], +} + +csuite_test { + name: "csuite-1p3p-pip-flickers", + test_config_template: "csuiteDefaultTemplate.xml", +} diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml new file mode 100644 index 000000000000..6d5423b28f39 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml @@ -0,0 +1,80 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wm.shell.flicker.pip"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Control test app's media session --> + <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- Enable bubble notification--> + <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + + <service android:name=".NotificationListener" + android:exported="true" + android:label="WMShellTestsNotificationListenerService" + android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> + <intent-filter> + <action android:name="android.service.notification.NotificationListenerService" /> + </intent-filter> + </service> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <!-- Enable mocking GPS location --> + <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.pip" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml new file mode 100644 index 000000000000..1df11369a049 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/pip/OWNERS index 172e24bf4574..172e24bf4574 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS +++ b/libs/WindowManager/Shell/tests/flicker/pip/OWNERS diff --git a/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml index ca182fa5a266..ca182fa5a266 100644 --- a/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml b/libs/WindowManager/Shell/tests/flicker/pip/res/xml/network_security_config.xml index 437871f1bb8f..4bd9ca049f55 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml +++ b/libs/WindowManager/Shell/tests/flicker/pip/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.wm.shell.flicker.bubble"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.wm.shell.flicker.bubble" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt index 943b16c33a98..a5c2c8988e70 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt @@ -81,8 +81,13 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : pipApp.launchViaIntent(wmHelper) tapl.goHome() SplitScreenUtils.enterSplit( - wmHelper, tapl, device, pipApp, secondAppForSplitScreen, - flicker.scenario.startRotation) + wmHelper, + tapl, + device, + pipApp, + secondAppForSplitScreen, + flicker.scenario.startRotation + ) pipApp.enableAutoEnterForPipActivity() } teardown { @@ -132,9 +137,7 @@ class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) : if (flicker.scenario.isLandscapeOrSeascapeAtStart) { flicker.assertWmVisibleRegion(pipApp) { // first check against landscape bounds then against portrait bounds - coversAtMost(displayBounds).then().coversAtMost( - portraitDisplayBounds - ) + coversAtMost(displayBounds).then().coversAtMost(portraitDisplayBounds) } } else { // always check against the display bounds which do not change during transition diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index 94e3959782ed..af2db12faf3a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -55,8 +55,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : - EnterPipTransition(flicker) { +open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } override val defaultEnterPip: FlickerBuilder.() -> Unit = { @@ -66,11 +65,7 @@ open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : } } - override val defaultTeardown: FlickerBuilder.() -> Unit = { - teardown { - pipApp.exit(wmHelper) - } - } + override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } } @FlakyTest(bugId = 293133362) @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt index 9256725c6180..9256725c6180 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt index 002c019eff93..002c019eff93 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index 820af9316aae..4cc9547ba2e3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -53,11 +53,7 @@ class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransiti } } - override val defaultTeardown: FlickerBuilder.() -> Unit = { - teardown { - pipApp.exit(wmHelper) - } - } + override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } } @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index 8207b85c3e0c..8207b85c3e0c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt index cc943678d492..cc943678d492 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt index 7da442901e40..7da442901e40 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt index 0ad9e4c61d83..0ad9e4c61d83 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt index 89a6c93e478c..89a6c93e478c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt index 8978af0088b8..8978af0088b8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt index 4776206724cc..4776206724cc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt index 425cbfaffedd..425cbfaffedd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt index 18f30d96938b..18f30d96938b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt index 4f27ceddd705..36047cca55ea 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt @@ -35,9 +35,7 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { - pipApp.changeAspectRatio() - } + transitions { pipApp.changeAspectRatio() } } @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt index c7f2786debd0..c7f2786debd0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt index cabc1cc0b023..cabc1cc0b023 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt index 0fd1b2c3f0de..381f947cbc84 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt @@ -42,8 +42,8 @@ class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { } /** - * Checks that the visible region area of [pipApp] decreases - * and then increases during the animation. + * Checks that the visible region area of [pipApp] decreases and then increases during the + * animation. */ @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index 1f69847e5481..1f69847e5481 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt index 308ece40402f..308ece40402f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt index c9a98c73e5e5..be5a27ac7dcc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt @@ -81,9 +81,8 @@ abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTran @Test override fun pipLayerReduces() { flicker.assertLayers { - val pipLayerList = this.layers { - standardAppHelper.layerMatchesAnyOf(it) && it.isVisible - } + val pipLayerList = + this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible } pipLayerList.zipWithNext { previous, current -> current.visibleRegion.notBiggerThan(previous.visibleRegion.region) } @@ -194,7 +193,8 @@ abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTran * transition */ @Postsubmit - @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() + @Test + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() /** * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive @@ -215,9 +215,7 @@ abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTran super.visibleWindowsShownMoreThanOneConsecutiveEntry() /** Checks that all parts of the screen are covered during the transition */ - @Postsubmit - @Test - override fun entireScreenCovered() = super.entireScreenCovered() + @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() companion object { /** diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt index d7ba3d57b548..4da52ef1272c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt @@ -69,20 +69,21 @@ open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition val mainHandler = Handler(Looper.getMainLooper()) var mockLocationEnabled = false - val updateLocation = object : Runnable { - override fun run() { - // early bail out if mocking location is not enabled - if (!mockLocationEnabled) return - val location = Location("Googleplex") - location.latitude = 37.42243438411294 - location.longitude = -122.08426281892311 - location.time = System.currentTimeMillis() - location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() - location.accuracy = 100f - locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location) - mainHandler.postDelayed(this, 5) + val updateLocation = + object : Runnable { + override fun run() { + // early bail out if mocking location is not enabled + if (!mockLocationEnabled) return + val location = Location("Googleplex") + location.latitude = 37.42243438411294 + location.longitude = -122.08426281892311 + location.time = System.currentTimeMillis() + location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() + location.accuracy = 100f + locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location) + mainHandler.postDelayed(this, 5) + } } - } override val defaultEnterPip: FlickerBuilder.() -> Unit = { setup { @@ -129,9 +130,7 @@ open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition } } - override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { tapl.goHome() } - } + override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } @Postsubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt index 596580547d59..5498e8c4f970 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt @@ -67,25 +67,18 @@ open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransit standardAppHelper.launchViaIntent( wmHelper, NetflixAppHelper.getNetflixWatchVideoIntent("70184207"), - ComponentNameMatcher( - NetflixAppHelper.PACKAGE_NAME, - NetflixAppHelper.WATCH_ACTIVITY - ) + ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, NetflixAppHelper.WATCH_ACTIVITY) ) standardAppHelper.waitForVideoPlaying() } } override val defaultTeardown: FlickerBuilder.() -> Unit = { - teardown { - standardAppHelper.exit(wmHelper) - } + teardown { standardAppHelper.exit(wmHelper) } } override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { - tapl.goHomeFromImmersiveFullscreenApp() - } + transitions { tapl.goHomeFromImmersiveFullscreenApp() } } @Postsubmit @@ -133,7 +126,8 @@ open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransit } @Postsubmit - @Test override fun statusBarWindowIsAlwaysVisible() { + @Test + override fun statusBarWindowIsAlwaysVisible() { // Netflix plays in immersive fullscreen mode, so taskbar will be gone at some point } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt index c370d91034fd..d8afc25caf71 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt @@ -70,14 +70,10 @@ open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransit } override val defaultTeardown: FlickerBuilder.() -> Unit = { - teardown { - standardAppHelper.exit(wmHelper) - } + teardown { standardAppHelper.exit(wmHelper) } } - override val thisTransition: FlickerBuilder.() -> Unit = { - transitions { tapl.goHome() } - } + override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } @Postsubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt index 751f2bc0807f..751f2bc0807f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt index 9c129e47efba..9c129e47efba 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt index 9450bdd2d894..9450bdd2d894 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt index 7e42bc11a9c1..7e42bc11a9c1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt index 7b7f1d7b5a82..7b7f1d7b5a82 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt index c6cbcd052fe0..c6cbcd052fe0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt index 2cb18f948f0e..2cb18f948f0e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt index 8a073abf032c..8a073abf032c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt index d4cd6da4acb1..d4cd6da4acb1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt index 4402e2153e9b..4402e2153e9b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt index 47bff8de377e..47bff8de377e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt index 4aee61ade10e..4aee61ade10e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt diff --git a/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto new file mode 100644 index 000000000000..406ada97a07d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto @@ -0,0 +1,75 @@ +# Copyright (C) 2023 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.android.wm.shell.flicker" + atrace_apps: "com.android.wm.shell.flicker.other" + atrace_apps: "com.android.wm.shell.flicker.bubbles" + atrace_apps: "com.android.wm.shell.flicker.pip" + atrace_apps: "com.android.wm.shell.flicker.splitscreen" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/libs/WindowManager/Shell/tests/flicker/service/Android.bp b/libs/WindowManager/Shell/tests/flicker/service/Android.bp new file mode 100644 index 000000000000..9b8cd94d56b2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/service/Android.bp @@ -0,0 +1,67 @@ +// +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "WMShellFlickerServicePlatinumTests-src", + srcs: [ + "src/**/platinum/*.kt", + "src/**/scenarios/*.kt", + "src/**/common/*.kt", + ], +} + +java_library { + name: "wm-shell-flicker-platinum-tests", + platform_apis: true, + optimize: { + enabled: false, + }, + srcs: [ + ":WMShellFlickerServicePlatinumTests-src", + ], + static_libs: [ + "wm-shell-flicker-utils", + ], +} + +android_test { + name: "WMShellFlickerServiceTests", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.service", + instrumentation_target_package: "com.android.wm.shell.flicker.service", + srcs: ["src/**/*.kt"], + static_libs: ["WMShellFlickerTestsBase"], +} + +android_test { + name: "WMShellFlickerServicePlatinumTests", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.service", + instrumentation_target_package: "com.android.wm.shell.flicker.service", + srcs: [":WMShellFlickerServicePlatinumTests-src"], + static_libs: ["WMShellFlickerTestsBase"], +} diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml new file mode 100644 index 000000000000..d54b6941d975 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml @@ -0,0 +1,77 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wm.shell.flicker.service"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Control test app's media session --> + <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- Enable bubble notification--> + <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + + <service android:name=".NotificationListener" + android:exported="true" + android:label="WMShellTestsNotificationListenerService" + android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> + <intent-filter> + <action android:name="android.service.notification.NotificationListenerService" /> + </intent-filter> + </service> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.service" + android:label="WindowManager Flicker Service Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml new file mode 100644 index 000000000000..1df11369a049 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml b/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml index 887d8db3042f..4bd9ca049f55 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml +++ b/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.wm.shell.flicker.splitscreen"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.wm.shell.flicker.splitscreen" - android:label="WindowManager Flicker Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt index 5f157856aa36..4bd79546b96d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt @@ -36,9 +36,7 @@ object Utils { fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain { return RuleChain.outerRule(ArtifactSaverRule()) .around(UnlockScreenRule()) - .around( - NavigationModeRule(navigationMode.value, false) - ) + .around(NavigationModeRule(navigationMode.value, false)) .around( LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false) ) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS index 3ab6a1ee061d..3ab6a1ee061d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt index a5c512267508..a5c512267508 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt index 092fb6720e57..092fb6720e57 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt index 69499b9b488b..69499b9b488b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt index bd627f4babaa..bd627f4babaa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt index a8f4d0a24c7e..a8f4d0a24c7e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt index cee9bbfb4aa4..cee9bbfb4aa4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt index c1b3aade11cd..c1b3aade11cd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt index c6e2e854ab89..c6e2e854ab89 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt index 169b5cfa3462..169b5cfa3462 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt index 412c011a3f17..412c011a3f17 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt index 6e4cf9f55cfd..6e4cf9f55cfd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt index cc2870213e8d..cc2870213e8d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt index 736604f02377..736604f02377 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt index 8df8dfaab071..8df8dfaab071 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt index 378f055ef830..378f055ef830 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt index b33d26288d33..b33d26288d33 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt index b1d3858b9dbb..b1d3858b9dbb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt index 6d824c74c1fe..6d824c74c1fe 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt index f1d3d0cf2716..f1d3d0cf2716 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt index a867bac8c0eb..a867bac8c0eb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt index 76247ba10037..76247ba10037 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt index e179da81e4db..e179da81e4db 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt index 20f554f7d154..20f554f7d154 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt index f7776ee3244a..f7776ee3244a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt index 4ff0b4362e60..4ff0b4362e60 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt index 930f31d1f348..930f31d1f348 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt index 3da61e5e310c..3da61e5e310c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt index 627ae1843314..627ae1843314 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt index c744103d49ba..c744103d49ba 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt index 11a4e02c5e37..11a4e02c5e37 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt index e37d806c7a14..e37d806c7a14 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt index 2a50912e0a5c..2a50912e0a5c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt index d5da1a8b558c..d5da1a8b558c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt index 7fdcb9be62ee..7fdcb9be62ee 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt index 308e954b86c1..308e954b86c1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt index 39e75bd25a71..39e75bd25a71 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt index e18da17175c0..e18da17175c0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt index 00d60e756ffa..00d60e756ffa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt index d7efbc8c0fd4..d7efbc8c0fd4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt index 4eece3f62d10..4eece3f62d10 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt index d96b056d8753..d96b056d8753 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt index 809b690e0861..809b690e0861 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt index bbdf2d728494..bbdf2d728494 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt index 5c29fd8fe57e..5c29fd8fe57e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt index a7398ebf56e8..a7398ebf56e8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt index eae88ad4ad09..eae88ad4ad09 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt index 7e8ee04a28fa..7e8ee04a28fa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt index 9295c330b879..9295c330b879 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt index 4b59e9fbd866..4b59e9fbd866 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt index 5ff36d4aabbb..5ff36d4aabbb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt index c0cb7219437b..c0cb7219437b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt index 8c140884aa50..8c140884aa50 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt index 7b6614b81c11..7b6614b81c11 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt index 5df5be9daa8b..5df5be9daa8b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt index 9d63003bf2a1..9d63003bf2a1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt index 9fa04b208ad1..9fa04b208ad1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt index 9386aa2b2cf0..9386aa2b2cf0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt index 5ef21672bfe0..5ef21672bfe0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt index 9caab9b5182a..9caab9b5182a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt index bf484e5cef98..bf484e5cef98 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt index 80ab24ddf9ef..80ab24ddf9ef 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt index 4c391047e853..4c391047e853 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt index f6d1afc05a5a..f6d1afc05a5a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt index db5a32a382fb..db5a32a382fb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt index d7b306c3be23..d7b306c3be23 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt index cc982d1ba860..cc982d1ba860 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt index fa12bb869467..fa12bb869467 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt index 2592fd40d902..2592fd40d902 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt index 983653b9b5ca..983653b9b5ca 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt index 068171d2e129..068171d2e129 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt index 64b75c5fd967..64b75c5fd967 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt index 179501089168..179501089168 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt index 7065846dc653..7065846dc653 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt index 251cb50de017..251cb50de017 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt index a9933bbe09fc..a9933bbe09fc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt diff --git a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto new file mode 100644 index 000000000000..406ada97a07d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto @@ -0,0 +1,75 @@ +# Copyright (C) 2023 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.android.wm.shell.flicker" + atrace_apps: "com.android.wm.shell.flicker.other" + atrace_apps: "com.android.wm.shell.flicker.bubbles" + atrace_apps: "com.android.wm.shell.flicker.pip" + atrace_apps: "com.android.wm.shell.flicker.splitscreen" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp new file mode 100644 index 000000000000..4629c5318366 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp @@ -0,0 +1,77 @@ +// +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "WMShellFlickerTestsSplitScreenBase-src", + srcs: [ + "src/**/benchmark/*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsSplitScreenGroup1-src", + srcs: [ + "src/**/A*.kt", + "src/**/B*.kt", + "src/**/C*.kt", + "src/**/D*.kt", + "src/**/E*.kt", + ], +} + +filegroup { + name: "WMShellFlickerTestsSplitScreenGroup2-src", + srcs: [ + "src/**/*.kt", + ], +} + +android_test { + name: "WMShellFlickerTestsSplitScreenGroup1", + defaults: ["WMShellFlickerTestsDefault"], + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.splitscreen", + instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + srcs: [ + ":WMShellFlickerTestsSplitScreenBase-src", + ":WMShellFlickerTestsSplitScreenGroup1-src", + ], + static_libs: ["WMShellFlickerTestsBase"], +} + +android_test { + name: "WMShellFlickerTestsSplitScreenGroup2", + manifest: "AndroidManifest.xml", + package_name: "com.android.wm.shell.flicker.splitscreen", + instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + srcs: [ + ":WMShellFlickerTestsSplitScreenBase-src", + ":WMShellFlickerTestsSplitScreenGroup2-src", + ], + exclude_srcs: [ + ":WMShellFlickerTestsSplitScreenGroup1-src", + ], + static_libs: ["WMShellFlickerTestsBase"], +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml new file mode 100644 index 000000000000..9ff2161daa51 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml @@ -0,0 +1,77 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.wm.shell.flicker.splitscreen"> + + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <!-- Read and write traces from external storage --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!-- Allow the test to write directly to /sdcard/ --> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> + <!-- Write secure settings --> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> + <!-- Capture screen contents --> + <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" /> + <!-- Enable / Disable tracing !--> + <uses-permission android:name="android.permission.DUMP" /> + <!-- Run layers trace --> + <uses-permission android:name="android.permission.HARDWARE_TEST"/> + <!-- Capture screen recording --> + <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/> + <!-- Workaround grant runtime permission exception from b/152733071 --> + <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/> + <uses-permission android:name="android.permission.READ_LOGS"/> + <!-- Force-stop test apps --> + <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/> + <!-- Control test app's media session --> + <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> + <!-- ATM.removeRootTasksWithActivityTypes() --> + <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> + <!-- Enable bubble notification--> + <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> + <!-- Allow the test to connect to perfetto trace processor --> + <uses-permission android:name="android.permission.INTERNET"/> + + <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> + <application android:requestLegacyExternalStorage="true" + android:networkSecurityConfig="@xml/network_security_config" + android:largeHeap="true"> + <uses-library android:name="android.test.runner"/> + + <service android:name=".NotificationListener" + android:exported="true" + android:label="WMShellTestsNotificationListenerService" + android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> + <intent-filter> + <action android:name="android.service.notification.NotificationListenerService" /> + </intent-filter> + </service> + + <!-- (b/197936012) Remove startup provider due to test timeout issue --> + <provider + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + tools:node="remove" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.splitscreen" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml new file mode 100644 index 000000000000..1df11369a049 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml @@ -0,0 +1,109 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Enable mocking GPS location by the test app --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/> + <option name="teardown-command" + value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/> + </target_preparer> + + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.service/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS index 3ab6a1ee061d..3ab6a1ee061d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml index c7aca1a72696..4bd9ca049f55 100644 --- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2023 The Android Open Source Project ~ @@ -14,11 +15,8 @@ ~ limitations under the License. --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.wm.shell.flicker.service"> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.wm.shell.flicker.service" - android:label="WindowManager Flicker Service Tests"> - </instrumentation> -</manifest> +<network-security-config> + <domain-config cleartextTrafficPermitted="true"> + <domain includeSubdomains="true">localhost</domain> + </domain-config> +</network-security-config> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index 6b971699d212..6b971699d212 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt index 51588569a8aa..51588569a8aa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index fc6c2b3d7ce7..fc6c2b3d7ce7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt index 8b1689a9d816..8b1689a9d816 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt index 99613f39060d..99613f39060d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt index 756a7fa4ba98..756a7fa4ba98 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt index 121b46acdb66..121b46acdb66 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt index 99deb9279271..99deb9279271 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index 212a4e3649dc..212a4e3649dc 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt index fac97c8cc8c4..fac97c8cc8c4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index 284c32ea110d..284c32ea110d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index 9e6448f0bec9..9e6448f0bec9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index 8e28712cd993..8e28712cd993 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt index fb0193b1830d..fb0193b1830d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt index 13875362a1c8..715a533a7bab 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt @@ -62,10 +62,22 @@ class SwitchBetweenSplitPairsNoPip(override val flicker: LegacyFlickerTest) : get() = { setup { tapl.goHome() - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp, - flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + thirdApp, + pipApp, + flicker.scenario.startRotation + ) pipApp.enableAutoEnterForPipActivity() SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index f3145c97a6f1..f3145c97a6f1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt index 3b9e53f9ce04..df1c9a2ec089 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt @@ -39,8 +39,16 @@ abstract class CopyContentInSplitBenchmark(override val flicker: LegacyFlickerTe protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:") protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - textEditApp, flicker.scenario.startRotation) } + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + textEditApp, + flicker.scenario.startRotation + ) + } transitions { SplitScreenUtils.copyContentInSplit( instrumentation, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt index 5fdde3ad23d2..d01eab060263 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt @@ -35,8 +35,16 @@ abstract class DismissSplitScreenByDividerBenchmark(override val flicker: Legacy SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) } + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + } transitions { if (tapl.isTablet) { SplitScreenUtils.dragDividerToDismissSplit( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt index b7f6bfe7efd6..e36bd33bd1fd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt @@ -36,8 +36,14 @@ abstract class DismissSplitScreenByGoHomeBenchmark(override val flicker: LegacyF protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, - flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) } transitions { tapl.goHome() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt index bb2a7aabed1b..050d389e978c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt @@ -37,8 +37,16 @@ abstract class DragDividerToResizeBenchmark(override val flicker: LegacyFlickerT SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) } + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + } transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt index 394864ad9d4d..394864ad9d4d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt index cd3fbab1497b..cd3fbab1497b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt index 3b3be84f9841..3b3be84f9841 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt index eff355987cc0..eff355987cc0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt index 5e5e7d7fc3c9..5e5e7d7fc3c9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt index a0e437c25aa7..a0e437c25aa7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt index 46b0bd226daf..e39c3c93d79a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt @@ -39,8 +39,16 @@ abstract class SwitchAppByDoubleTapDividerBenchmark(override val flicker: Legacy SplitScreenBase(flicker) { protected val thisTransition: FlickerBuilder.() -> Unit get() = { - setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) } + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + } transitions { SplitScreenUtils.doubleTapDividerToSwitch(device) wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt index baf76932c7ac..32284ba41aee 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt @@ -39,8 +39,14 @@ abstract class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Le protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) thirdApp.launchViaIntent(wmHelper) wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt index 33b55f1a57d8..a926ec903f58 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt @@ -37,8 +37,14 @@ abstract class SwitchBackToSplitFromHomeBenchmark(override val flicker: LegacyFl protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt index b79dfb5f0665..d2e1d5294aa1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt @@ -37,8 +37,14 @@ abstract class SwitchBackToSplitFromRecentBenchmark(override val flicker: Legacy protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) tapl.goHome() wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt index 0204d754585a..9d6b25174c13 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt @@ -39,10 +39,22 @@ abstract class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlic protected val thisTransition: FlickerBuilder.() -> Unit get() = { setup { - SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, - secondaryApp, flicker.scenario.startRotation) - SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, - flicker.scenario.startRotation) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + thirdApp, + fourthApp, + flicker.scenario.startRotation + ) SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp) } transitions { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt index e71834de7123..e71834de7123 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto new file mode 100644 index 000000000000..406ada97a07d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto @@ -0,0 +1,75 @@ +# Copyright (C) 2023 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.android.wm.shell.flicker" + atrace_apps: "com.android.wm.shell.flicker.other" + atrace_apps: "com.android.wm.shell.flicker.bubbles" + atrace_apps: "com.android.wm.shell.flicker.pip" + atrace_apps: "com.android.wm.shell.flicker.splitscreen" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt index 735fbfb341f5..568650d71872 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 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. diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt index 6b3cfaf33c05..c31b9e2c22c7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt @@ -24,6 +24,7 @@ import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.IComponentMatcher import android.tools.common.traces.component.IComponentNameMatcher import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule import android.tools.device.traces.parsers.WindowManagerStateHelper import android.tools.device.traces.parsers.toFlickerComponent import android.view.InputDevice @@ -42,7 +43,6 @@ import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary import org.junit.Assert.assertNotNull -import android.tools.device.flicker.rules.ChangeDisplayOrientationRule object SplitScreenUtils { private const val TIMEOUT_MS = 3_000L @@ -153,15 +153,10 @@ object SplitScreenUtils { } else { val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled() tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview - val home = tapl.workspace - .switchToOverview() + val home = tapl.workspace.switchToOverview() tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks ChangeDisplayOrientationRule.setRotation(rotation) - home.currentTask - .tapMenu() - .tapSplitMenuItem() - .currentTask - .open() + home.currentTask.tapMenu().tapSplitMenuItem().currentTask.open() } SystemClock.sleep(TIMEOUT_MS) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index 02c9d306f4bf..2ac72affbb0c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -98,4 +98,21 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim // The animation should be empty when it is behind starting window. assertEquals(0, animator.getDuration()); } + + @Test + public void testInvalidCustomAnimation() { + final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0) + .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) + .build(); + info.setAnimationOptions(TransitionInfo.AnimationOptions + .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */, + 0 /* backgroundColor */, false /* overrideTaskTransition */)); + final Animator animator = mAnimRunner.createAnimator( + info, mStartTransaction, mFinishTransaction, + () -> mFinishCallback.onTransitionFinished(null /* wct */), + new ArrayList<>()); + + // An invalid custom animation is equivalent to jump-cut. + assertEquals(0, animator.getDuration()); + } } diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ff1eedb8eacb..da728f90e8e0 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -144,6 +144,7 @@ cc_defaults { "libsync", "libui", "aconfig_text_flags_c_lib", + "server_configurable_flags", ], static_libs: [ "libEGL_blobCache", diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index a8d170d00ef7..fd276419f5e5 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -242,6 +242,47 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } } +SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const { + const DirtyStack* frame = mHead; + Matrix4 transform; + SkRect pretransformResult = bounds; + while (true) { + SkRect currentBounds = pretransformResult; + pretransformResult.setEmpty(); + switch (frame->type) { + case TransformRenderNode: { + const RenderProperties& props = frame->renderNode->properties(); + // Perform clipping + if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) { + if (!currentBounds.intersect( + SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { + currentBounds.setEmpty(); + } + } + + // apply all transforms + mapRect(props, currentBounds, &pretransformResult); + frame->renderNode->applyViewPropertyTransforms(transform); + } break; + case TransformMatrix4: + mapRect(frame->matrix4, currentBounds, &pretransformResult); + transform.multiply(*frame->matrix4); + break; + default: + pretransformResult = currentBounds; + break; + } + if (frame->prev == frame) break; + frame = frame->prev; + } + SkRect result; + Matrix4 globalToLocal; + globalToLocal.loadInverse(transform); + mapRect(&globalToLocal, pretransformResult, &result); + *outMatrix = transform; + return result; +} + void DamageAccumulator::dirty(float left, float top, float right, float bottom) { mHead->pendingDirty.join({left, top, right, bottom}); } diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index c4249af392d3..30bf7063a627 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -61,6 +61,8 @@ public: void computeCurrentTransform(Matrix4* outMatrix) const; + SkRect computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const; + void finish(SkRect* totalDirty); struct StretchResult { diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 5e5eb4a51b35..ad600d0bab93 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -20,15 +20,26 @@ #ifdef __ANDROID__ #include "HWUIProperties.sysprop.h" #endif -#include "src/core/SkTraceEventCommon.h" +#include <android-base/properties.h> +#include <cutils/compiler.h> +#include <log/log.h> #include <algorithm> #include <cstdlib> #include <optional> -#include <android-base/properties.h> -#include <cutils/compiler.h> -#include <log/log.h> +#include "src/core/SkTraceEventCommon.h" + +#ifdef __ANDROID__ +#include <com_android_graphics_hwui_flags.h> +namespace hwui_flags = com::android::graphics::hwui::flags; +#else +namespace hwui_flags { +constexpr bool clip_surfaceviews() { + return false; +} +} // namespace hwui_flags +#endif namespace android { namespace uirenderer { @@ -92,6 +103,8 @@ bool Properties::isSystemOrPersistent = false; float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number +bool Properties::clipSurfaceViews = false; + StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI; DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized; @@ -159,6 +172,9 @@ bool Properties::load() { // call isDrawingEnabled to force loading of the property isDrawingEnabled(); + clipSurfaceViews = + base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index bb477449fff0..bca57e9e4678 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -325,6 +325,8 @@ public: static float maxHdrHeadroomOn8bit; + static bool clipSurfaceViews; + static StretchEffectBehavior getStretchEffectBehavior() { return stretchEffectBehavior; } diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index caffdfc907f7..ef4dce57bf46 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -17,19 +17,19 @@ #ifndef ANDROID_GRAPHICS_PAINT_H_ #define ANDROID_GRAPHICS_PAINT_H_ -#include "Typeface.h" - -#include <cutils/compiler.h> - #include <SkFont.h> #include <SkPaint.h> #include <SkSamplingOptions.h> -#include <string> - -#include <minikin/FontFamily.h> +#include <cutils/compiler.h> #include <minikin/FamilyVariant.h> +#include <minikin/FontFamily.h> +#include <minikin/FontFeature.h> #include <minikin/Hyphenator.h> +#include <string> + +#include "Typeface.h" + namespace android { class BlurDrawLooper; @@ -82,11 +82,15 @@ public: float getWordSpacing() const { return mWordSpacing; } - void setFontFeatureSettings(const std::string& fontFeatureSettings) { - mFontFeatureSettings = fontFeatureSettings; + void setFontFeatureSettings(std::string_view fontFeatures) { + mFontFeatureSettings = minikin::FontFeature::parse(fontFeatures); } - std::string getFontFeatureSettings() const { return mFontFeatureSettings; } + void resetFontFeatures() { mFontFeatureSettings.clear(); } + + const std::vector<minikin::FontFeature>& getFontFeatureSettings() const { + return mFontFeatureSettings; + } void setMinikinLocaleListId(uint32_t minikinLocaleListId) { mMinikinLocaleListId = minikinLocaleListId; @@ -170,7 +174,7 @@ private: float mLetterSpacing = 0; float mWordSpacing = 0; - std::string mFontFeatureSettings; + std::vector<minikin::FontFeature> mFontFeatureSettings; uint32_t mMinikinLocaleListId; std::optional<minikin::FamilyVariant> mFamilyVariant; uint32_t mHyphenEdit = 0; diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 8c71d6fc7860..d84b73d1a1ca 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -33,6 +33,7 @@ #include <cassert> #include <cstring> #include <memory> +#include <string_view> #include <vector> #include "ColorFilter.h" @@ -690,10 +691,11 @@ namespace PaintGlue { jstring settings) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); if (!settings) { - paint->setFontFeatureSettings(std::string()); + paint->resetFontFeatures(); } else { ScopedUtfChars settingsChars(env, settings); - paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size())); + paint->setFontFeatureSettings( + std::string_view(settingsChars.c_str(), settingsChars.size())); } } diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index 2a218a25913d..a1b05c186ec0 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -568,6 +568,7 @@ static void android_view_RenderNode_forceEndAnimators(JNIEnv* env, jobject clazz struct { jclass clazz; jmethodID callPositionChanged; + jmethodID callPositionChanged2; jmethodID callApplyStretch; jmethodID callPositionLost; } gPositionListener; @@ -589,14 +590,25 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override { if (CC_UNLIKELY(!mListener || !info.updateWindowPositions)) return; - Matrix4 transform; - info.damageAccumulator->computeCurrentTransform(&transform); const RenderProperties& props = node.properties(); + const bool enableClip = Properties::clipSurfaceViews; - uirenderer::Rect bounds(props.getWidth(), props.getHeight()); + Matrix4 transform; + SkIRect clipBounds; + if (enableClip) { + uirenderer::Rect initialClipBounds; + props.getClippingRectForFlags(props.getClippingFlags(), &initialClipBounds); + clipBounds = + info.damageAccumulator + ->computeClipAndTransform(initialClipBounds.toSkRect(), &transform) + .roundOut(); + } else { + info.damageAccumulator->computeCurrentTransform(&transform); + } bool useStretchShader = Properties::getStretchEffectBehavior() != StretchEffectBehavior::UniformScale; // Compute the transform bounds first before calculating the stretch + uirenderer::Rect bounds(props.getWidth(), props.getHeight()); transform.mapRect(bounds); bool hasStretch = useStretchShader && info.stretchEffectCount; @@ -614,10 +626,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, bounds.roundOut(); } - if (mPreviousPosition == bounds) { + if (mPreviousPosition == bounds && mPreviousClip == clipBounds) { return; } mPreviousPosition = bounds; + mPreviousClip = clipBounds; ATRACE_NAME("Update SurfaceView position"); @@ -629,11 +642,23 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, // In particular if the app removes a view from the view tree before // this callback is dispatched, then we lose the position // information for this frame. - jboolean keepListening = env->CallStaticBooleanMethod( - gPositionListener.clazz, gPositionListener.callPositionChanged, mListener, - static_cast<jlong>(info.canvasContext.getFrameNumber()), - static_cast<jint>(bounds.left), static_cast<jint>(bounds.top), - static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom)); + jboolean keepListening; + if (!enableClip) { + keepListening = env->CallStaticBooleanMethod( + gPositionListener.clazz, gPositionListener.callPositionChanged, mListener, + static_cast<jlong>(info.canvasContext.getFrameNumber()), + static_cast<jint>(bounds.left), static_cast<jint>(bounds.top), + static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom)); + } else { + keepListening = env->CallStaticBooleanMethod( + gPositionListener.clazz, gPositionListener.callPositionChanged2, mListener, + static_cast<jlong>(info.canvasContext.getFrameNumber()), + static_cast<jint>(bounds.left), static_cast<jint>(bounds.top), + static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom), + static_cast<jint>(clipBounds.fLeft), static_cast<jint>(clipBounds.fTop), + static_cast<jint>(clipBounds.fRight), + static_cast<jint>(clipBounds.fBottom)); + } if (!keepListening) { env->DeleteGlobalRef(mListener); mListener = nullptr; @@ -738,6 +763,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, JavaVM* mVm; jobject mListener; uirenderer::Rect mPreviousPosition; + uirenderer::Rect mPreviousClip; }; RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); @@ -866,6 +892,8 @@ int register_android_view_RenderNode(JNIEnv* env) { gPositionListener.clazz = MakeGlobalRefOrDie(env, clazz); gPositionListener.callPositionChanged = GetStaticMethodIDOrDie( env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z"); + gPositionListener.callPositionChanged2 = GetStaticMethodIDOrDie( + env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIII)Z"); gPositionListener.callApplyStretch = GetStaticMethodIDOrDie( env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z"); gPositionListener.callPositionLost = GetStaticMethodIDOrDie( diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index a4890ede8faa..ad963dd913cf 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -19,6 +19,8 @@ #include "DeferredLayerUpdater.h" #include "hwui/Paint.h" +#include <hwui/MinikinSkia.h> +#include <hwui/Typeface.h> #include <minikin/Layout.h> #include <pipeline/skia/SkiaOpenGLPipeline.h> #include <pipeline/skia/SkiaVulkanPipeline.h> @@ -179,5 +181,13 @@ SkRect TestUtils::getLocalClipBounds(const SkCanvas* canvas) { return outlineInLocalCoord; } +SkFont TestUtils::defaultFont() { + const std::shared_ptr<minikin::MinikinFont>& minikinFont = + Typeface::resolveDefault(nullptr)->fFontCollection->getFamilyAt(0)->getFont(0)->baseTypeface(); + SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(minikinFont.get())->GetSkTypeface(); + LOG_ALWAYS_FATAL_IF(skTypeface == nullptr); + return SkFont(sk_ref_sp(skTypeface)); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index ffc664c2e1bc..0ede902b1b95 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -30,6 +30,7 @@ #include <SkBitmap.h> #include <SkColor.h> +#include <SkFont.h> #include <SkImageInfo.h> #include <SkRefCnt.h> @@ -353,6 +354,8 @@ public: static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; } + static SkFont defaultFont(); + private: static std::unordered_map<int, CallCounts> sMockFunctorCounts; diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index 4a5d9468cd88..97d4c8214cde 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -57,7 +57,7 @@ class ListViewAnimation : public TestListViewSceneBase { 128 * 3; paint.setColor(bgDark ? Color::White : Color::Grey_700); - SkFont font; + SkFont font = TestUtils::defaultFont(); font.setSize(size / 2); char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp index bb95490c1d39..159541c11c64 100644 --- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp @@ -102,7 +102,7 @@ private: 128 * 3; paint.setColor(bgDark ? Color::White : Color::Grey_700); - SkFont font; + SkFont font = TestUtils::defaultFont(); font.setSize(size / 2); char charToShow = 'A' + (rand() % 26); const SkPoint pos = {SkIntToScalar(size / 2), diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 937151b1aff4..8ad35876989d 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -19,6 +19,7 @@ package android.media; import static android.media.MediaRouter2Utils.toUniqueId; import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_POLICIES_DEVICE_AND_BLUETOOTH_CONTROLLER; +import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -306,6 +307,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_TABLET = 1004; /** @@ -316,6 +318,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_TABLET_DOCKED = 1005; /** @@ -326,6 +329,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_COMPUTER = 1006; /** @@ -336,6 +340,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_GAME_CONSOLE = 1007; /** @@ -346,6 +351,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_CAR = 1008; /** @@ -356,6 +362,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_SMARTWATCH = 1009; /** @@ -366,6 +373,7 @@ public final class MediaRoute2Info implements Parcelable { * * @see #getType */ + @FlaggedApi(FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES) public static final int TYPE_REMOTE_SMARTPHONE = 1010; /** diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index 386534b80925..dd1df475d95b 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -15,29 +15,36 @@ flag { } flag { - namespace: "media_solutions" name: "enable_audio_policies_device_and_bluetooth_controller" + namespace: "media_solutions" description: "Use Audio Policies implementation for device and Bluetooth route controllers." bug: "280576228" } flag { - namespace: "media_solutions" name: "disable_screen_off_broadcast_receiver" + namespace: "media_solutions" description: "Disables the broadcast receiver that prevents scanning when the screen is off." bug: "304234628" } flag { - namespace: "media_solutions" name: "fallback_to_default_handling_when_media_session_has_fixed_volume_handling" + namespace: "media_solutions" description: "Fallbacks to the default handling for volume adjustment when media session has fixed volume handling and its app is in the foreground and setting a media controller." bug: "293743975" } flag { - namespace: "media_solutions" name: "enable_waiting_state_for_system_session_creation_request" + namespace: "media_solutions" description: "Introduces a waiting state for the session creation request and prevents it from early failing when the selectedRoute from the bluetooth stack doesn't match the pending request route id." bug: "307723189" } + +flag { + name: "enable_new_media_route_2_info_types" + namespace: "media_solutions" + description: "Enables the following type constants in MediaRoute2Info: CAR, COMPUTER, GAME_CONSOLE, SMARTPHONE, SMARTWATCH, TABLET, TABLET_DOCKED. Note that this doesn't gate any behavior. It only guards some API int symbols." + bug: "301713440" +} diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index 24efbd14bc02..a7ec6c692416 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -212,4 +212,9 @@ interface IMediaProjectionManager { @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") oneway void notifyAppSelectorDisplayed(int hostUid); + + @EnforcePermission("MANAGE_MEDIA_PROJECTION") + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.MANAGE_MEDIA_PROJECTION)") + void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode); } diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig new file mode 100644 index 000000000000..a73d1ff72a17 --- /dev/null +++ b/media/java/android/media/tv/flags/media_tv.aconfig @@ -0,0 +1,8 @@ +package: "android.media.tv.flags" + +flag { + name: "broadcast_visibility_types" + namespace: "media_tv" + description: "Constants for standardizing broadcast visibility types." + bug: "222402395" +}
\ No newline at end of file diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index 1088acef0fb0..4992ef1e1c00 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -290,7 +290,14 @@ public class InstallInstalling extends AlertActivity { broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); - session.commit(pendingIntent.getIntentSender()); + try { + session.commit(pendingIntent.getIntentSender()); + } catch (Exception e) { + Log.e(LOG_TAG, "Cannot install package: ", e); + launchFailure(PackageInstaller.STATUS_FAILURE, + PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); + return; + } mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } else { diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt index 9f7f040be7ce..aa148b022b92 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt @@ -21,7 +21,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material3.PrimaryTabRow +import androidx.compose.material3.TabRow import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -43,7 +43,7 @@ fun SettingsPager(titles: List<String>, content: @Composable (page: Int) -> Unit val coroutineScope = rememberCoroutineScope() val pagerState = rememberPagerState { titles.size } - PrimaryTabRow( + TabRow( selectedTabIndex = pagerState.currentPage, modifier = Modifier.padding(horizontal = SettingsDimension.itemPaddingEnd), containerColor = Color.Transparent, diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 22131df60b0d..96029c813528 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1027,6 +1027,13 @@ <!-- Settings item title to select whether to disable cache for transcoding. [CHAR LIMIT=85] --> <string name="transcode_disable_cache">Disable transcoding cache</string> + <!-- Developer settings title: widevine settings screen. [CHAR LIMIT=50] --> + <string name="widevine_settings_title">Widevine settings</string> + <!-- Developer settings title: select whether to enable Force L3 fallback. [CHAR LIMIT=50] --> + <string name="force_l3_fallback_title">Force L3 fallback</string> + <!-- Developer settings summary: select whether to enable Force L3 fallback.[CHAR LIMIT=NONE] --> + <string name="force_l3_fallback_summary">Select to force L3 fallback</string> + <!-- Services settings screen, setting option name for the user to go to the screen to view running services --> <string name="runningservices_settings_title">Running services</string> <!-- Services settings screen, setting option summary for the user to go to the screen to view running services --> diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManager.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManager.java deleted file mode 100644 index 117b48ff852d..000000000000 --- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeEnablerManager.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2017 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.settingslib.inputmethod; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.text.TextUtils; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; - -import androidx.preference.Preference; -import androidx.preference.PreferenceCategory; -import androidx.preference.PreferenceFragment; -import androidx.preference.PreferenceScreen; -import androidx.preference.TwoStatePreference; - -import com.android.settingslib.R; - -import java.text.Collator; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -public class InputMethodAndSubtypeEnablerManager implements Preference.OnPreferenceChangeListener { - - private final PreferenceFragment mFragment; - - private boolean mHaveHardKeyboard; - private final HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap = - new HashMap<>(); - private final HashMap<String, TwoStatePreference> mAutoSelectionPrefsMap = new HashMap<>(); - private InputMethodManager mImm; - // TODO: Change mInputMethodInfoList to Map - private List<InputMethodInfo> mInputMethodInfoList; - private final Collator mCollator = Collator.getInstance(); - - public InputMethodAndSubtypeEnablerManager(PreferenceFragment fragment) { - mFragment = fragment; - mImm = fragment.getContext().getSystemService(InputMethodManager.class); - - mInputMethodInfoList = mImm.getInputMethodList(); - } - - public void init(PreferenceFragment fragment, String targetImi, PreferenceScreen root) { - final Configuration config = fragment.getResources().getConfiguration(); - mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY); - - for (final InputMethodInfo imi : mInputMethodInfoList) { - // Add subtype preferences of this IME when it is specified or no IME is specified. - if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) { - addInputMethodSubtypePreferences(fragment, imi, root); - } - } - } - - public void refresh(Context context, PreferenceFragment fragment) { - // Refresh internal states in mInputMethodSettingValues to keep the latest - // "InputMethodInfo"s and "InputMethodSubtype"s - InputMethodSettingValuesWrapper - .getInstance(context).refreshAllInputMethodAndSubtypes(); - InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(fragment, context.getContentResolver(), - mInputMethodInfoList, mInputMethodAndSubtypePrefsMap); - updateAutoSelectionPreferences(); - } - - public void save(Context context, PreferenceFragment fragment) { - InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(fragment, context.getContentResolver(), - mInputMethodInfoList, mHaveHardKeyboard); - } - - @Override - public boolean onPreferenceChange(final Preference pref, final Object newValue) { - if (!(newValue instanceof Boolean)) { - return true; // Invoke default behavior. - } - final boolean isChecking = (Boolean) newValue; - for (final String imiId : mAutoSelectionPrefsMap.keySet()) { - // An auto select subtype preference is changing. - if (mAutoSelectionPrefsMap.get(imiId) == pref) { - final TwoStatePreference autoSelectionPref = (TwoStatePreference) pref; - autoSelectionPref.setChecked(isChecking); - // Enable or disable subtypes depending on the auto selection preference. - setAutoSelectionSubtypesEnabled(imiId, autoSelectionPref.isChecked()); - return false; - } - } - // A subtype preference is changing. - if (pref instanceof InputMethodSubtypePreference) { - final InputMethodSubtypePreference subtypePref = (InputMethodSubtypePreference) pref; - subtypePref.setChecked(isChecking); - if (!subtypePref.isChecked()) { - // It takes care of the case where no subtypes are explicitly enabled then the auto - // selection preference is going to be checked. - updateAutoSelectionPreferences(); - } - return false; - } - return true; // Invoke default behavior. - } - - private void addInputMethodSubtypePreferences(PreferenceFragment fragment, InputMethodInfo imi, - final PreferenceScreen root) { - Context prefContext = fragment.getPreferenceManager().getContext(); - - final int subtypeCount = imi.getSubtypeCount(); - if (subtypeCount <= 1) { - return; - } - final String imiId = imi.getId(); - final PreferenceCategory keyboardSettingsCategory = - new PreferenceCategory(prefContext); - root.addPreference(keyboardSettingsCategory); - final PackageManager pm = prefContext.getPackageManager(); - final CharSequence label = imi.loadLabel(pm); - - keyboardSettingsCategory.setTitle(label); - keyboardSettingsCategory.setKey(imiId); - // TODO: Use toggle Preference if images are ready. - final TwoStatePreference autoSelectionPref = - new SwitchWithNoTextPreference(prefContext); - mAutoSelectionPrefsMap.put(imiId, autoSelectionPref); - keyboardSettingsCategory.addPreference(autoSelectionPref); - autoSelectionPref.setOnPreferenceChangeListener(this); - - final PreferenceCategory activeInputMethodsCategory = - new PreferenceCategory(prefContext); - activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes); - root.addPreference(activeInputMethodsCategory); - - CharSequence autoSubtypeLabel = null; - final ArrayList<Preference> subtypePreferences = new ArrayList<>(); - for (int index = 0; index < subtypeCount; ++index) { - final InputMethodSubtype subtype = imi.getSubtypeAt(index); - if (subtype.overridesImplicitlyEnabledSubtype()) { - if (autoSubtypeLabel == null) { - autoSubtypeLabel = InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence( - subtype, prefContext, imi); - } - } else { - final Preference subtypePref = new InputMethodSubtypePreference( - prefContext, subtype, imi); - subtypePreferences.add(subtypePref); - } - } - subtypePreferences.sort((lhs, rhs) -> { - if (lhs instanceof InputMethodSubtypePreference) { - return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator); - } - return lhs.compareTo(rhs); - }); - for (final Preference pref : subtypePreferences) { - activeInputMethodsCategory.addPreference(pref); - pref.setOnPreferenceChangeListener(this); - InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref); - } - mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences); - if (TextUtils.isEmpty(autoSubtypeLabel)) { - autoSelectionPref.setTitle( - R.string.use_system_language_to_select_input_method_subtypes); - } else { - autoSelectionPref.setTitle(autoSubtypeLabel); - } - } - - private boolean isNoSubtypesExplicitlySelected(final String imiId) { - final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId); - for (final Preference pref : subtypePrefs) { - if (pref instanceof TwoStatePreference && ((TwoStatePreference) pref).isChecked()) { - return false; - } - } - return true; - } - - private void setAutoSelectionSubtypesEnabled(final String imiId, - final boolean autoSelectionEnabled) { - final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId); - if (autoSelectionPref == null) { - return; - } - autoSelectionPref.setChecked(autoSelectionEnabled); - final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId); - for (final Preference pref : subtypePrefs) { - if (pref instanceof TwoStatePreference) { - // When autoSelectionEnabled is true, all subtype prefs need to be disabled with - // implicitly checked subtypes. In case of false, all subtype prefs need to be - // enabled. - pref.setEnabled(!autoSelectionEnabled); - if (autoSelectionEnabled) { - ((TwoStatePreference) pref).setChecked(false); - } - } - } - if (autoSelectionEnabled) { - InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( - mFragment, mFragment.getContext().getContentResolver(), - mInputMethodInfoList, mHaveHardKeyboard); - updateImplicitlyEnabledSubtypes(imiId); - } - } - - private void updateImplicitlyEnabledSubtypes(final String targetImiId) { - // When targetImiId is null, apply to all subtypes of all IMEs - for (final InputMethodInfo imi : mInputMethodInfoList) { - final String imiId = imi.getId(); - final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId); - // No need to update implicitly enabled subtypes when the user has unchecked the - // "subtype auto selection". - if (autoSelectionPref == null || !autoSelectionPref.isChecked()) { - continue; - } - if (imiId.equals(targetImiId) || targetImiId == null) { - updateImplicitlyEnabledSubtypesOf(imi); - } - } - } - - private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi) { - final String imiId = imi.getId(); - final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId); - final List<InputMethodSubtype> implicitlyEnabledSubtypes = - mImm.getEnabledInputMethodSubtypeList(imi, true); - if (subtypePrefs == null || implicitlyEnabledSubtypes == null) { - return; - } - for (final Preference pref : subtypePrefs) { - if (!(pref instanceof TwoStatePreference)) { - continue; - } - final TwoStatePreference subtypePref = (TwoStatePreference) pref; - subtypePref.setChecked(false); - for (final InputMethodSubtype subtype : implicitlyEnabledSubtypes) { - final String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode(); - if (subtypePref.getKey().equals(implicitlyEnabledSubtypePrefKey)) { - subtypePref.setChecked(true); - break; - } - } - } - } - - private void updateAutoSelectionPreferences() { - for (final String imiId : mInputMethodAndSubtypePrefsMap.keySet()) { - setAutoSelectionSubtypesEnabled(imiId, isNoSubtypesExplicitlySelected(imiId)); - } - updateImplicitlyEnabledSubtypes(null /* targetImiId */ /* check */); - } -} diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index a4a9290b16ab..adebdcdcf26a 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -33,6 +33,7 @@ android_library { ], static_libs: [ "device_config_service_flags_java", + "libaconfig_java_proto_lite", "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayUtils", ], diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index 969f1fde604e..976ba215d349 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -22,6 +22,8 @@ import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; import static com.android.providers.settings.Flags.supportOverrides; +import android.aconfig.Aconfig.parsed_flag; +import android.aconfig.Aconfig.parsed_flags; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.AttributionSource; @@ -39,12 +41,13 @@ import android.provider.DeviceConfigShellCommandHandler; import android.provider.Settings; import android.provider.Settings.Config.SyncDisabledMode; import android.provider.UpdatableDeviceConfigServiceReadiness; +import android.util.Slog; import com.android.internal.util.FastPrintWriter; import java.io.File; import java.io.FileDescriptor; -import java.io.FileNotFoundException; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -56,18 +59,17 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Scanner; /** * Receives shell commands from the command line related to device config flags, and dispatches them * to the SettingsProvider. */ public final class DeviceConfigService extends Binder { - private static final List<String> aconfigTextProtoFilesOnDevice = List.of( - "/system/etc/aconfig_flags.textproto", - "/system_ext/etc/aconfig_flags.textproto", - "/system_ext/etc/aconfig_flags.textproto", - "/vendor/etc/aconfig_flags.textproto"); + private static final List<String> sAconfigTextProtoFilesOnDevice = List.of( + "/system/etc/aconfig_flags.pb", + "/system_ext/etc/aconfig_flags.pb", + "/system_ext/etc/aconfig_flags.pb", + "/vendor/etc/aconfig_flags.pb"); private static final List<String> PRIVATE_NAMESPACES = List.of( "device_config_overrides", @@ -76,6 +78,8 @@ public final class DeviceConfigService extends Binder { final SettingsProvider mProvider; + private static final String TAG = "DeviceConfigService"; + public DeviceConfigService(SettingsProvider provider) { mProvider = provider; } @@ -94,62 +98,55 @@ public final class DeviceConfigService extends Binder { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - final IContentProvider iprovider = mProvider.getIContentProvider(); - pw.println("DeviceConfig flags:"); - for (String line : MyShellCommand.listAll(iprovider)) { - pw.println(line); - } - - ArrayList<String> missingFiles = new ArrayList<String>(); - for (String fileName : aconfigTextProtoFilesOnDevice) { - File aconfigFile = new File(fileName); - if (!aconfigFile.exists()) { - missingFiles.add(fileName); + final IContentProvider iprovider = mProvider.getIContentProvider(); + pw.println("DeviceConfig flags:"); + for (String line : MyShellCommand.listAll(iprovider)) { + pw.println(line); } - } - if (missingFiles.isEmpty()) { - pw.println("\nAconfig flags:"); - for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) { - pw.println(name); + ArrayList<String> missingFiles = new ArrayList<String>(); + for (String fileName : sAconfigTextProtoFilesOnDevice) { + File aconfigFile = new File(fileName); + if (!aconfigFile.exists()) { + missingFiles.add(fileName); + } } - } else { - pw.println("\nFailed to dump aconfig flags due to missing files:"); - for (String fileName : missingFiles) { - pw.println(fileName); + + if (missingFiles.isEmpty()) { + pw.println("\nAconfig flags:"); + for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) { + pw.println(name); + } + } else { + pw.println("\nFailed to dump aconfig flags due to missing files:"); + for (String fileName : missingFiles) { + pw.println(fileName); + } } - } } private static HashSet<String> getAconfigFlagNamesInDeviceConfig() { HashSet<String> nameSet = new HashSet<String>(); - for (String fileName : aconfigTextProtoFilesOnDevice) { - try{ - File aconfigFile = new File(fileName); - String packageName = ""; - String namespace = ""; - String name = ""; - - try (Scanner scanner = new Scanner(aconfigFile)) { - while (scanner.hasNextLine()) { - String data = scanner.nextLine().replaceAll("\\s+",""); - if (data.startsWith("package:\"")) { - packageName = data.substring(9, data.length()-1); - } else if (data.startsWith("name:\"")) { - name = data.substring(6, data.length()-1); - } else if (data.startsWith("namespace:\"")) { - namespace = data.substring(11, data.length()-1); - nameSet.add(namespace + "/" + packageName + "." + name); + try { + for (String fileName : sAconfigTextProtoFilesOnDevice) { + byte[] contents = (new FileInputStream(fileName)).readAllBytes(); + parsed_flags parsedFlags = parsed_flags.parseFrom(contents); + if (parsedFlags == null) { + Slog.e(TAG, "failed to parse aconfig protobuf from " + fileName); + continue; } - } - } - } catch (FileNotFoundException e) { - continue; - } + for (parsed_flag flag : parsedFlags.getParsedFlagList()) { + String namespace = flag.getNamespace(); + String packageName = flag.getPackage(); + String name = flag.getName(); + nameSet.add(namespace + "/" + packageName + "." + name); + } + } + } catch (IOException e) { + Slog.e(TAG, "failed to read aconfig protobuf", e); } - - return nameSet; + return nameSet; } private void callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 17cc9f8135f4..89c6ecc4630b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -60,7 +60,6 @@ systemui_compose_java_defaults { // except for SystemUI-core. // Copied from compose/features/Android.bp. static_libs: [ - "CommunalLayoutLib", "PlatformComposeCore", "PlatformComposeSceneTransitionLayout", @@ -366,6 +365,7 @@ filegroup { "tests/src/com/android/systemui/qs/pipeline/shared/TileSpecTest.kt", "tests/src/com/android/systemui/qs/tiles/base/**/*.kt", "tests/src/com/android/systemui/qs/tiles/viewmodel/**/*.kt", + "tests/src/com/android/systemui/qs/tiles/impl/**/*.kt", /* Authentication */ "tests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt", diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index 9e06872797dc..21263a92ae26 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -6,7 +6,7 @@ flag { name: "floating_menu_animated_tuck" namespace: "accessibility" description: "Sets up animations for tucking/untucking and adjusts clipbounds." - bug: "24592044" + bug: "297556899" } flag { diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index c26d5f53dee9..8eff9bfcf099 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -51,3 +51,11 @@ flag { description: "Enables the scene container framework go/flexiglass." bug: "283121968" } + +flag { + name: "visual_interruptions_refactor" + namespace: "systemui" + description: "Enables the refactored version of the code to decide when notifications " + "HUN, bubble, pulse, or FSI." + bug: "261728888" +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index ab4db451406d..f7d9056c33dc 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -114,7 +114,13 @@ class ActivityLaunchAnimator( private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f) /** The time we wait before timing out the remote animation after starting the intent. */ - private const val LAUNCH_TIMEOUT = 1000L + private const val LAUNCH_TIMEOUT = 1_000L + + /** + * The time we wait before we Log.wtf because the remote animation was neither started or + * cancelled by WM. + */ + private const val LONG_LAUNCH_TIMEOUT = 5_000L private fun createPositionXInterpolator(): Interpolator { val path = @@ -247,7 +253,7 @@ class ActivityLaunchAnimator( // If we expect an animation, post a timeout to cancel it in case the remote animation is // never started. if (willAnimate) { - runnerDelegate.postTimeout() + runnerDelegate.postTimeouts() // Hide the keyguard using the launch animation instead of the default unlock animation. if (hideKeyguardWithAnimation) { @@ -578,21 +584,41 @@ class ActivityLaunchAnimator( private var cancelled = false private var animation: LaunchAnimator.Animation? = null - // A timeout to cancel the remote animation if it is not started within X milliseconds after - // the intent was started. - // - // Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise - // it will be automatically converted when posted and we wouldn't be able to remove it after - // posting it. + /** + * A timeout to cancel the launch animation if the remote animation is not started or + * cancelled within [LAUNCH_TIMEOUT] milliseconds after the intent was started. + * + * Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise + * it will be automatically converted when posted and we wouldn't be able to remove it after + * posting it. + */ private var onTimeout = Runnable { onAnimationTimedOut() } + /** + * A long timeout to Log.wtf (signaling a bug in WM) when the remote animation wasn't + * started or cancelled within [LONG_LAUNCH_TIMEOUT] milliseconds after the intent was + * started. + */ + private var onLongTimeout = Runnable { + Log.wtf( + TAG, + "The remote animation was neither cancelled or started within $LONG_LAUNCH_TIMEOUT" + ) + } + @UiThread - internal fun postTimeout() { - timeoutHandler?.postDelayed(onTimeout, LAUNCH_TIMEOUT) + internal fun postTimeouts() { + if (timeoutHandler != null) { + timeoutHandler.postDelayed(onTimeout, LAUNCH_TIMEOUT) + timeoutHandler.postDelayed(onLongTimeout, LONG_LAUNCH_TIMEOUT) + } } - private fun removeTimeout() { - timeoutHandler?.removeCallbacks(onTimeout) + private fun removeTimeouts() { + if (timeoutHandler != null) { + timeoutHandler.removeCallbacks(onTimeout) + timeoutHandler.removeCallbacks(onLongTimeout) + } } @UiThread @@ -603,7 +629,7 @@ class ActivityLaunchAnimator( nonApps: Array<out RemoteAnimationTarget>?, callback: IRemoteAnimationFinishedCallback? ) { - removeTimeout() + removeTimeouts() // The animation was started too late and we already notified the controller that it // timed out. @@ -653,7 +679,6 @@ class ActivityLaunchAnimator( val window = findRootTaskIfPossible(apps) if (window == null) { Log.i(TAG, "Aborting the animation as no window is opening") - removeTimeout() iCallback?.invoke() if (DEBUG_LAUNCH_ANIMATION) { @@ -890,11 +915,13 @@ class ActivityLaunchAnimator( } private fun onAnimationTimedOut() { + // The remote animation was cancelled by WM, so we already cancelled the launch + // animation. if (cancelled) { return } - Log.wtf(TAG, "Remote animation timed out") + Log.w(TAG, "Remote animation timed out") timedOut = true if (DEBUG_LAUNCH_ANIMATION) { @@ -906,13 +933,15 @@ class ActivityLaunchAnimator( @UiThread override fun onAnimationCancelled() { + removeTimeouts() + + // The short timeout happened, so we already cancelled the launch animation. if (timedOut) { return } Log.i(TAG, "Remote animation was cancelled") cancelled = true - removeTimeout() animation?.cancel() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index b8fb26406801..87a8c35388fa 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -4,8 +4,14 @@ import android.appwidget.AppWidgetHostView import android.os.Bundle import android.util.SizeF import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.material3.Card import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -13,16 +19,12 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.integerResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.model.CommunalContentUiModel import com.android.systemui.communal.ui.viewmodel.CommunalViewModel -import com.android.systemui.res.R @Composable fun CommunalHub( @@ -34,68 +36,91 @@ fun CommunalHub( Box( modifier = modifier.fillMaxSize().background(Color.White), ) { - CommunalGridLayout( - modifier = Modifier.align(Alignment.CenterStart), - layoutConfig = - CommunalGridLayoutConfig( - gridColumnSize = dimensionResource(R.dimen.communal_grid_column_size), - gridGutter = dimensionResource(R.dimen.communal_grid_gutter_size), - gridHeight = dimensionResource(R.dimen.communal_grid_height), - gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card), - ), - communalCards = if (showTutorial) tutorialContent else widgetContent.map(::contentCard), - ) + LazyHorizontalGrid( + modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart), + rows = GridCells.Fixed(CommunalContentSize.FULL.span), + horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing), + verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing), + ) { + if (showTutorial) { + items( + count = tutorialContentSizes.size, + // TODO(b/308148193): a more scalable solution for unique ids. + key = { index -> "tutorial_$index" }, + span = { index -> GridItemSpan(tutorialContentSizes[index].span) }, + ) { index -> + TutorialCard( + modifier = + Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()), + ) + } + } else { + items( + count = widgetContent.size, + key = { index -> widgetContent[index].id }, + span = { index -> GridItemSpan(widgetContent[index].size.span) }, + ) { index -> + val widget = widgetContent[index] + ContentCard( + modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()), + model = widget, + ) + } + } + } } } -private val tutorialContent = - listOf( - tutorialCard(CommunalGridLayoutCard.Size.FULL), - tutorialCard(CommunalGridLayoutCard.Size.THIRD), - tutorialCard(CommunalGridLayoutCard.Size.THIRD), - tutorialCard(CommunalGridLayoutCard.Size.THIRD), - tutorialCard(CommunalGridLayoutCard.Size.HALF), - tutorialCard(CommunalGridLayoutCard.Size.HALF), - tutorialCard(CommunalGridLayoutCard.Size.HALF), - tutorialCard(CommunalGridLayoutCard.Size.HALF), - ) - -private fun tutorialCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard { - return object : CommunalGridLayoutCard() { - override val supportedSizes = listOf(size) - - @Composable - override fun Content(modifier: Modifier, size: SizeF) { - Card(modifier = modifier, content = {}) - } - } +// A placeholder for tutorial content. +@Composable +private fun TutorialCard(modifier: Modifier = Modifier) { + Card(modifier = modifier, content = {}) } -private fun contentCard(model: CommunalContentUiModel): CommunalGridLayoutCard { - return object : CommunalGridLayoutCard() { - override val supportedSizes = listOf(convertToCardSize(model.size)) - override val priority = model.priority +@Composable +private fun ContentCard( + model: CommunalContentUiModel, + modifier: Modifier = Modifier, +) { + AndroidView( + modifier = modifier, + factory = { + model.view.apply { + if (this is AppWidgetHostView) { + val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value) + updateAppWidgetSize(Bundle.EMPTY, listOf(size)) + } + } + }, + ) +} - @Composable - override fun Content(modifier: Modifier, size: SizeF) { - AndroidView( - modifier = modifier, - factory = { - model.view.apply { - if (this is AppWidgetHostView) { - updateAppWidgetSize(Bundle(), listOf(size)) - } - } - }, - ) - } +private fun CommunalContentSize.dp(): Dp { + return when (this) { + CommunalContentSize.FULL -> Dimensions.CardHeightFull + CommunalContentSize.HALF -> Dimensions.CardHeightHalf + CommunalContentSize.THIRD -> Dimensions.CardHeightThird } } -private fun convertToCardSize(size: CommunalContentSize): CommunalGridLayoutCard.Size { - return when (size) { - CommunalContentSize.FULL -> CommunalGridLayoutCard.Size.FULL - CommunalContentSize.HALF -> CommunalGridLayoutCard.Size.HALF - CommunalContentSize.THIRD -> CommunalGridLayoutCard.Size.THIRD - } +// Sizes for the tutorial placeholders. +private val tutorialContentSizes = + listOf( + CommunalContentSize.FULL, + CommunalContentSize.THIRD, + CommunalContentSize.THIRD, + CommunalContentSize.THIRD, + CommunalContentSize.HALF, + CommunalContentSize.HALF, + CommunalContentSize.HALF, + CommunalContentSize.HALF, + ) + +private object Dimensions { + val CardWidth = 464.dp + val CardHeightFull = 630.dp + val CardHeightHalf = 307.dp + val CardHeightThird = 199.dp + val GridHeight = CardHeightFull + val Spacing = 16.dp } diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml deleted file mode 100644 index 56587b99b80b..000000000000 --- a/packages/SystemUI/res/layout/volume_dnd_icon.xml +++ /dev/null @@ -1,31 +0,0 @@ -<!-- - Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/dnd_icon" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom" - android:layout_marginTop="6dp" - android:layout_marginBottom="6dp"> - - <ImageView - android:layout_width="14dp" - android:layout_height="14dp" - android:layout_gravity="center" - android:src="@*android:drawable/ic_qs_dnd" - android:tint="?android:attr/textColorTertiary"/> -</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 85122baa3fec..e69eced0de45 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -18,6 +18,7 @@ package com.android.systemui.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION; import android.animation.Animator; @@ -63,7 +64,6 @@ import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.res.R; import com.android.systemui.biometrics.AuthController.ScaleFactorProvider; import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor; import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; @@ -76,8 +76,8 @@ import com.android.systemui.biometrics.ui.binder.Spaghetti; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.res.R; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -280,7 +280,6 @@ public class AuthContainerView extends LinearLayout // TODO(b/251476085): remove Config and further decompose these properties out of view classes AuthContainerView(@NonNull Config config, - @NonNull FeatureFlags featureFlags, @NonNull CoroutineScope applicationCoroutineScope, @Nullable List<FingerprintSensorPropertiesInternal> fpProps, @Nullable List<FaceSensorPropertiesInternal> faceProps, @@ -295,7 +294,7 @@ public class AuthContainerView extends LinearLayout @NonNull Provider<CredentialViewModel> credentialViewModelProvider, @NonNull @Background DelayableExecutor bgExecutor, @NonNull VibratorHelper vibratorHelper) { - this(config, featureFlags, applicationCoroutineScope, fpProps, faceProps, + this(config, applicationCoroutineScope, fpProps, faceProps, wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils, jankMonitor, promptSelectorInteractor, promptCredentialInteractor, promptViewModel, credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor, @@ -304,7 +303,6 @@ public class AuthContainerView extends LinearLayout @VisibleForTesting AuthContainerView(@NonNull Config config, - @NonNull FeatureFlags featureFlags, @NonNull CoroutineScope applicationCoroutineScope, @Nullable List<FingerprintSensorPropertiesInternal> fpProps, @Nullable List<FaceSensorPropertiesInternal> faceProps, @@ -368,7 +366,7 @@ public class AuthContainerView extends LinearLayout showPrompt(config, layoutInflater, promptViewModel, Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds), Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds), - vibratorHelper, featureFlags); + vibratorHelper); // TODO: De-dupe the logic with AuthCredentialPasswordView setOnKeyListener((v, keyCode, event) -> { @@ -390,8 +388,7 @@ public class AuthContainerView extends LinearLayout @NonNull PromptViewModel viewModel, @Nullable FingerprintSensorPropertiesInternal fpProps, @Nullable FaceSensorPropertiesInternal faceProps, - @NonNull VibratorHelper vibratorHelper, - @NonNull FeatureFlags featureFlags + @NonNull VibratorHelper vibratorHelper ) { if (Utils.isBiometricAllowed(config.mPromptInfo)) { mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication( @@ -407,7 +404,7 @@ public class AuthContainerView extends LinearLayout getJankListener(view, TRANSIT, BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS), mBackgroundView, mBiometricCallback, mApplicationCoroutineScope, - vibratorHelper, featureFlags); + vibratorHelper); // TODO(b/251476085): migrate these dependencies if (fpProps != null && fpProps.isAnyUdfpsType()) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index a64e862000fc..05db56f9eafb 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -78,7 +78,6 @@ import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.data.repository.BiometricType; import com.android.systemui.statusbar.CommandQueue; @@ -120,7 +119,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, private final Handler mHandler; private final Context mContext; - private final FeatureFlags mFeatureFlags; private final Execution mExecution; private final CommandQueue mCommandQueue; private final ActivityTaskManager mActivityTaskManager; @@ -743,7 +741,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } @Inject public AuthController(Context context, - @NonNull FeatureFlags featureFlags, @Application CoroutineScope applicationCoroutineScope, Execution execution, CommandQueue commandQueue, @@ -770,7 +767,6 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, @NonNull UdfpsUtils udfpsUtils, @NonNull VibratorHelper vibratorHelper) { mContext = context; - mFeatureFlags = featureFlags; mExecution = execution; mUserManager = userManager; mLockPatternUtils = lockPatternUtils; @@ -1316,7 +1312,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, config.mRequestId = requestId; config.mSensorIds = sensorIds; config.mScaleProvider = this::getScaleFactor; - return new AuthContainerView(config, mFeatureFlags, mApplicationCoroutineScope, mFpProps, mFaceProps, + return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps, wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils, mInteractionJankMonitor, mPromptCredentialInteractor, mPromptSelectorInteractor, viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index ac48b6a2b11e..32d9067bd9e4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -49,8 +49,6 @@ import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode import com.android.systemui.biometrics.ui.viewmodel.PromptMessage import com.android.systemui.biometrics.ui.viewmodel.PromptSize import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper @@ -78,7 +76,6 @@ object BiometricViewBinder { legacyCallback: Spaghetti.Callback, applicationScope: CoroutineScope, vibratorHelper: VibratorHelper, - featureFlags: FeatureFlags, ): Spaghetti { val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!! @@ -380,13 +377,11 @@ object BiometricViewBinder { } // Play haptics - if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { - launch { - viewModel.hapticsToPlay.collect { hapticFeedbackConstant -> - if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) { - vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant) - viewModel.clearHaptics() - } + launch { + viewModel.hapticsToPlay.collect { hapticFeedbackConstant -> + if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) { + vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant) + viewModel.clearHaptics() } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index e49b4a7bbce9..647aaf392ed8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -29,10 +29,7 @@ import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.res.R -import com.android.systemui.statusbar.VibratorHelper import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope @@ -52,9 +49,7 @@ class PromptViewModel constructor( displayStateInteractor: DisplayStateInteractor, promptSelectorInteractor: PromptSelectorInteractor, - private val vibrator: VibratorHelper, @Application context: Context, - private val featureFlags: FeatureFlags, ) { /** The set of modalities available for this prompt */ val modalities: Flow<BiometricModalities> = @@ -339,7 +334,7 @@ constructor( _message.value = PromptMessage.Error(message) if (hapticFeedback) { - vibrator.error(failedModality) + vibrateOnError() } messageJob?.cancel() @@ -457,7 +452,7 @@ constructor( _message.value = PromptMessage.Empty if (!needsUserConfirmation) { - vibrator.success(modality) + vibrateOnSuccess() } messageJob?.cancel() @@ -495,7 +490,7 @@ constructor( _isAuthenticated.value = authState.asExplicitlyConfirmed() _message.value = PromptMessage.Empty - vibrator.success(authState.authenticatedModality) + vibrateOnSuccess() messageJob?.cancel() messageJob = null @@ -530,20 +525,12 @@ constructor( _forceLargeSize.value = true } - private fun VibratorHelper.success(modality: BiometricModality) { - if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { - _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM - } else { - vibrateAuthSuccess("$TAG, modality = $modality BP::success") - } + private fun vibrateOnSuccess() { + _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM } - private fun VibratorHelper.error(modality: BiometricModality = BiometricModality.None) { - if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { - _hapticsToPlay.value = HapticFeedbackConstants.REJECT - } else { - vibrateAuthError("$TAG, modality = $modality BP::error") - } + private fun vibrateOnError() { + _hapticsToPlay.value = HapticFeedbackConstants.REJECT } /** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java index f77f98956cbf..a79a654aedc2 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java @@ -75,5 +75,8 @@ public interface FalsingCollector { /** Indicates an a11y action was made. */ void onA11yAction(); + + /** Initialize the class. */ + void init(); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java index 0dfaf0f4318d..d6b9a119e31c 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java @@ -23,6 +23,10 @@ import javax.inject.Inject; /** */ public class FalsingCollectorFake implements FalsingCollector { + @Override + public void init() { + } + @Inject public FalsingCollectorFake() { } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java index a6b073da2530..12df96e41b2c 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -32,13 +32,14 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionStateManager; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; import com.android.systemui.util.sensors.ThresholdSensorEvent; @@ -65,9 +66,11 @@ class FalsingCollectorImpl implements FalsingCollector { private final ProximitySensor mProximitySensor; private final StatusBarStateController mStatusBarStateController; private final KeyguardStateController mKeyguardStateController; + private final Lazy<ShadeInteractor> mShadeInteractorLazy; private final BatteryController mBatteryController; private final DockManager mDockManager; private final DelayableExecutor mMainExecutor; + private final JavaAdapter mJavaAdapter; private final SystemClock mSystemClock; private final Lazy<SelectedUserInteractor> mUserInteractor; @@ -136,10 +139,11 @@ class FalsingCollectorImpl implements FalsingCollector { ProximitySensor proximitySensor, StatusBarStateController statusBarStateController, KeyguardStateController keyguardStateController, - ShadeExpansionStateManager shadeExpansionStateManager, + Lazy<ShadeInteractor> shadeInteractorLazy, BatteryController batteryController, DockManager dockManager, @Main DelayableExecutor mainExecutor, + JavaAdapter javaAdapter, SystemClock systemClock, Lazy<SelectedUserInteractor> userInteractor) { mFalsingDataProvider = falsingDataProvider; @@ -149,12 +153,17 @@ class FalsingCollectorImpl implements FalsingCollector { mProximitySensor = proximitySensor; mStatusBarStateController = statusBarStateController; mKeyguardStateController = keyguardStateController; + mShadeInteractorLazy = shadeInteractorLazy; mBatteryController = batteryController; mDockManager = dockManager; mMainExecutor = mainExecutor; + mJavaAdapter = javaAdapter; mSystemClock = systemClock; mUserInteractor = userInteractor; + } + @Override + public void init() { mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME); @@ -163,7 +172,10 @@ class FalsingCollectorImpl implements FalsingCollector { mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); - shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged); + mJavaAdapter.alwaysCollectFlow( + mShadeInteractorLazy.get().isQsExpanded(), + this::onQsExpansionChanged + ); mBatteryController.addCallback(mBatteryListener); mDockManager.addListener(mDockEventListener); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt index e5b404f30889..c5d8c795853d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt @@ -23,6 +23,10 @@ import javax.inject.Inject @SysUISingleton class FalsingCollectorNoOp @Inject constructor() : FalsingCollector { + override fun init() { + logDebug("NOOP: init") + } + override fun onSuccessfulUnlock() { logDebug("NOOP: onSuccessfulUnlock") } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt new file mode 100644 index 000000000000..b79538aac3e6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.classifier + +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +/** Initializes classes related to falsing. */ +@SysUISingleton +class FalsingCoreStartable @Inject constructor(val falsingCollector: FalsingCollector) : + CoreStartable { + override fun start() { + falsingCollector.init() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java index 2729b7b0a4fd..af467ef1319b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -19,9 +19,9 @@ package com.android.systemui.classifier; import android.content.res.Resources; import android.view.ViewConfiguration; -import com.android.systemui.res.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlags; import com.android.systemui.statusbar.phone.NotificationTapHelper; @@ -37,7 +37,7 @@ import java.util.Set; import javax.inject.Named; /** Dagger Module for Falsing. */ -@Module +@Module(includes = {FalsingStartModule.class}) public interface FalsingModule { String BRIGHT_LINE_GESTURE_CLASSIFERS = "bright_line_gesture_classifiers"; String SINGLE_TAP_TOUCH_SLOP = "falsing_single_tap_touch_slop"; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt new file mode 100644 index 000000000000..a9f8f37fffa9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.classifier + +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +interface FalsingStartModule { + /** */ + @Binds + @IntoMap + @ClassKey(FalsingCoreStartable::class) + fun bindFalsingCoreStartable(falsingCoreStartable: FalsingCoreStartable?): CoreStartable? +} diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt index b2bc06f0ae29..48d374207388 100644 --- a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt +++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt @@ -20,4 +20,7 @@ package com.android.systemui.common.shared.model data class SharedNotificationContainerPosition( val top: Float = 0f, val bottom: Float = 0f, + + /** Whether any modifications to top/bottom are smoothly animated */ + val animate: Boolean = false, ) diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt index 39a6476929ed..c903709aa2ee 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt @@ -16,14 +16,19 @@ package com.android.systemui.communal.shared.model -/** Supported sizes for communal content in the layout grid. */ -enum class CommunalContentSize { +/** + * Supported sizes for communal content in the layout grid. + * + * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents + * HALF, 2 represents THIRD, and 1 represents SIXTH. + */ +enum class CommunalContentSize(val span: Int) { /** Content takes the full height of the column. */ - FULL, + FULL(6), /** Content takes half of the height of the column. */ - HALF, + HALF(3), /** Content takes a third of the height of the column. */ - THIRD, + THIRD(2), } diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt index 98060dc1dceb..b60dc2a21699 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt @@ -9,7 +9,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize * This model stays in the UI layer. */ data class CommunalContentUiModel( + val id: String, val view: View, - val size: CommunalContentSize, - val priority: Int, + val size: CommunalContentSize = CommunalContentSize.HALF, ) diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index 25c64eafe255..390b580bad28 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -20,7 +20,6 @@ import android.appwidget.AppWidgetHost import android.content.Context import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor -import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.ui.model.CommunalContentUiModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -42,16 +41,16 @@ constructor( /** List of widgets to be displayed in the communal hub. */ val widgetContent: Flow<List<CommunalContentUiModel>> = - communalInteractor.widgetContent.map { - it.map { + communalInteractor.widgetContent.map { widgets -> + widgets.map Widget@{ widget -> // TODO(b/306406256): As adding and removing widgets functionalities are // supported, cache the host views so they're not recreated each time. - val hostView = appWidgetHost.createView(context, it.appWidgetId, it.providerInfo) - return@map CommunalContentUiModel( + val hostView = + appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo) + return@Widget CommunalContentUiModel( + // TODO(b/308148193): a more scalable solution for unique ids. + id = "widget_${widget.appWidgetId}", view = hostView, - priority = it.priority, - // All widgets have HALF size. - size = CommunalContentSize.HALF, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index 00d95c02c172..0f038e10dd4e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -37,8 +37,6 @@ import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.KeyguardStateController @@ -59,7 +57,6 @@ class ControlActionCoordinatorImpl @Inject constructor( private val controlsMetricsLogger: ControlsMetricsLogger, private val vibrator: VibratorHelper, private val controlsSettingsRepository: ControlsSettingsRepository, - private val featureFlags: FeatureFlags, ) : ControlActionCoordinator { private var dialog: Dialog? = null private var pendingAction: Action? = null @@ -123,17 +120,12 @@ class ControlActionCoordinatorImpl @Inject constructor( } override fun drag(cvh: ControlViewHolder, isEdge: Boolean) { - if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) { - val constant = - if (isEdge) - HapticFeedbackConstants.SEGMENT_TICK - else - HapticFeedbackConstants.SEGMENT_FREQUENT_TICK - vibrator.performHapticFeedback(cvh.layout, constant) - } else { - val effect = if (isEdge) Vibrations.rangeEdgeEffect else Vibrations.rangeMiddleEffect - vibrate(effect) - } + val constant = + if (isEdge) + HapticFeedbackConstants.SEGMENT_TICK + else + HapticFeedbackConstants.SEGMENT_FREQUENT_TICK + vibrator.performHapticFeedback(cvh.layout, constant) } override fun setValue(cvh: ControlViewHolder, templateId: String, newValue: Float) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt deleted file mode 100644 index 29b7e985fabc..000000000000 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.controls.ui - -import android.os.VibrationEffect -import android.os.VibrationEffect.Composition.PRIMITIVE_TICK - -object Vibrations { - val rangeEdgeEffect = initRangeEdgeEffect() - val rangeMiddleEffect = initRangeMiddleEffect() - - private fun initRangeEdgeEffect(): VibrationEffect { - val composition = VibrationEffect.startComposition() - composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) - return composition.compose() - } - - private fun initRangeMiddleEffect(): VibrationEffect { - val composition = VibrationEffect.startComposition() - composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.1f) - return composition.compose() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 1dd4abfa0767..236c5b8ed2d7 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -48,7 +48,6 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule; -import com.android.systemui.statusbar.events.StatusBarEventsModule; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpModule; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -99,7 +98,6 @@ import javax.inject.Named; ReferenceScreenshotModule.class, RotationLockModule.class, SceneContainerFrameworkModule.class, - StatusBarEventsModule.class, StartCentralSurfacesModule.class, VolumeModule.class, WallpaperModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index a41bb2f67ad2..7915088b4642 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -96,6 +96,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.connectivity.ConnectivityModule; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule; +import com.android.systemui.statusbar.events.StatusBarEventsModule; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -209,6 +210,7 @@ import javax.inject.Named; SettingsUtilModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, + StatusBarEventsModule.class, StatusBarModule.class, StatusBarPipelineModule.class, StatusBarPolicyModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 4cade77c8312..323ed9871dc3 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -261,6 +261,13 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi } //TODO: brightnessfloat change usages to float. private int clampToUserSetting(int brightness) { + int screenBrightnessModeSetting = mSystemSettings.getIntForUser( + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); + if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) { + return brightness; + } + int userSetting = mSystemSettings.getIntForUser( Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE, UserHandle.USER_CURRENT); diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index f4a9f739d187..fa9866163646 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -400,8 +400,7 @@ object Flags { // 600- status bar // TODO(b/291315866): Tracking Bug - @JvmField val SIGNAL_CALLBACK_DEPRECATION = - unreleasedFlag("signal_callback_deprecation", teamfood = true) + @JvmField val SIGNAL_CALLBACK_DEPRECATION = releasedFlag("signal_callback_deprecation") // TODO(b/301610137): Tracking bug @JvmField val NEW_NETWORK_SLICE_UI = unreleasedFlag("new_network_slice_ui", teamfood = true) @@ -410,19 +409,15 @@ object Flags { val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS = releasedFlag("filter_provisioning_network_subscriptions") - // TODO(b/265892345): Tracking Bug - val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip") - // TODO(b/292533677): Tracking Bug - val WIFI_TRACKER_LIB_FOR_WIFI_ICON = - unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true) + val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon") // TODO(b/293863612): Tracking Bug @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON = releasedFlag("incompatible_charging_battery_icon") // TODO(b/293585143): Tracking Bug - val INSTANT_TETHER = unreleasedFlag("instant_tether", teamfood = true) + val INSTANT_TETHER = releasedFlag("instant_tether") // TODO(b/294588085): Tracking Bug val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 4d5c503d1c4e..67a12b06de0f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -22,6 +22,8 @@ import android.view.View import android.view.View.OnLayoutChangeListener import android.view.ViewGroup import android.view.ViewGroup.OnHierarchyChangeListener +import android.view.WindowInsets +import android.view.WindowInsets.Type import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.internal.jank.InteractionJankMonitor @@ -242,11 +244,18 @@ object KeyguardRootViewBinder { } ) + view.setOnApplyWindowInsetsListener { v: View, insets: WindowInsets -> + val insetTypes = WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout() + viewModel.topInset = insets.getInsetsIgnoringVisibility(insetTypes).top + insets + } + return object : DisposableHandle { override fun dispose() { disposableHandle.dispose() view.removeOnLayoutChangeListener(onLayoutChangeListener) view.setOnHierarchyChangeListener(null) + view.setOnApplyWindowInsetsListener(null) childViews.clear() } } @@ -288,7 +297,6 @@ object KeyguardRootViewBinder { oldBottom: Int ) { val nsslPlaceholder = v.findViewById(R.id.nssl_placeholder) as View? - if (nsslPlaceholder != null) { // After layout, ensure the notifications are positioned correctly viewModel.onSharedNotificationContainerPositionChanged( @@ -296,6 +304,11 @@ object KeyguardRootViewBinder { nsslPlaceholder.bottom.toFloat(), ) } + + val ksv = v.findViewById(R.id.keyguard_status_view) as View? + if (ksv != null) { + viewModel.statusViewTop = ksv.top + } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 1f98082c4065..e12da53287ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -65,7 +65,12 @@ constructor( */ private val previewMode = MutableStateFlow(PreviewMode()) - public var clockControllerProvider: Provider<ClockController>? = null + var clockControllerProvider: Provider<ClockController>? = null + + /** System insets that keyguard needs to stay out of */ + var topInset: Int = 0 + /** Status view top, without translation added in */ + var statusViewTop: Int = 0 val burnInLayerVisibility: Flow<Int> = keyguardTransitionInteractor.startedKeyguardState @@ -102,9 +107,12 @@ constructor( scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolation), ) } else { + // Ensure the desired translation doesn't encroach on the top inset + val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolation).toInt() + val translationY = -(statusViewTop - Math.max(topInset, statusViewTop + burnInY)) BurnInModel( translationX = MathUtils.lerp(0, burnIn.translationX, interpolation).toInt(), - translationY = MathUtils.lerp(0, burnIn.translationY, interpolation).toInt(), + translationY = translationY, scale = MathUtils.lerp(burnIn.scale, 1f, 1f - interpolation), scaleClockOnly = true, ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt index 8103152dda37..13271c32c52f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt @@ -21,8 +21,8 @@ import android.os.Handler import android.os.Looper import android.provider.Settings import android.view.View +import android.widget.Switch import com.android.internal.logging.MetricsLogger -import com.android.systemui.res.R import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -34,6 +34,7 @@ import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.dialog.InternetDialogFactory +import com.android.systemui.res.R import com.android.systemui.statusbar.connectivity.AccessPointController import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel @@ -96,6 +97,7 @@ constructor( override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) { state.label = mContext.resources.getString(R.string.quick_settings_internet_label) + state.expandedAccessibilityClassName = Switch::class.java.name model.applyTo(state, mContext) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt new file mode 100644 index 000000000000..dfeb65b7c5aa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.custom.data.entity + +import android.graphics.drawable.Icon + +sealed interface CustomTileDefaults { + + data object Error : CustomTileDefaults + data class Result( + val icon: Icon, + val label: CharSequence, + ) : CustomTileDefaults +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt new file mode 100644 index 000000000000..1546ec2c54bd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.custom.data.repository + +import android.content.ComponentName +import android.content.Context +import android.content.pm.PackageManager +import android.content.pm.ServiceInfo +import android.graphics.drawable.Icon +import android.os.UserHandle +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults +import com.android.systemui.qs.tiles.impl.di.QSTileScope +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn +import kotlinx.coroutines.withContext + +/** Gets a label and an icon for a custom tile based on its package. */ +interface CustomTileDefaultsRepository { + + /** + * Returns [CustomTileDefaults] for a specified [user]. An updated value may be emitted as a + * response for [requestNewDefaults]. + * + * @see requestNewDefaults + */ + fun defaults(user: UserHandle): Flow<CustomTileDefaults> + + /** + * Requests the new default from the [PackageManager]. The result is cached until the input of + * this method changes or [force] == true is passed. + * + * Listen to [defaults] to get the loaded result + */ + fun requestNewDefaults( + user: UserHandle, + componentName: ComponentName, + force: Boolean = false, + ) +} + +@QSTileScope +class CustomTileDefaultsRepositoryImpl +@Inject +constructor( + private val context: Context, + @Application applicationScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, +) : CustomTileDefaultsRepository { + + private val defaultsRequests = + MutableSharedFlow<DefaultsRequest>( + replay = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) + + private val defaults: SharedFlow<DefaultsResult> = + defaultsRequests + .distinctUntilChanged { old, new -> + if (new.force) { + // force update should always pass + false + } else { + old == new + } + } + .map { DefaultsResult(it.user, loadDefaults(it.user, it.componentName)) } + .shareIn(applicationScope, SharingStarted.WhileSubscribed(), replay = 1) + + override fun defaults(user: UserHandle): Flow<CustomTileDefaults> = + defaults.filter { it.user == user }.map { it.data } + + override fun requestNewDefaults( + user: UserHandle, + componentName: ComponentName, + force: Boolean, + ) { + defaultsRequests.tryEmit(DefaultsRequest(user, componentName, force)) + } + + private suspend fun loadDefaults( + user: UserHandle, + componentName: ComponentName + ): CustomTileDefaults = + withContext(backgroundDispatcher) { + try { + val userContext = context.createContextAsUser(user, 0) + val info = componentName.getServiceInfo(userContext.packageManager) + + val iconRes = if (info.icon == NO_ICON_RES) info.applicationInfo.icon else info.icon + if (iconRes == NO_ICON_RES) { + return@withContext CustomTileDefaults.Error + } + + CustomTileDefaults.Result( + Icon.createWithResource(componentName.packageName, iconRes), + info.loadLabel(userContext.packageManager) + ) + } catch (e: PackageManager.NameNotFoundException) { + CustomTileDefaults.Error + } + } + + private fun ComponentName.getServiceInfo( + packageManager: PackageManager, + ): ServiceInfo { + val isSystemApp = packageManager.getApplicationInfo(packageName, 0).isSystemApp + var flags = + (PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE) + if (isSystemApp) { + flags = flags or PackageManager.MATCH_DISABLED_COMPONENTS + } + return packageManager.getServiceInfo(this, flags) + } + + private data class DefaultsRequest( + val user: UserHandle, + val componentName: ComponentName, + val force: Boolean = false, + ) + + private data class DefaultsResult(val user: UserHandle, val data: CustomTileDefaults) + + private companion object { + + const val NO_ICON_RES = 0 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt index ccff8afe7628..482bf9bcd051 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt @@ -23,6 +23,8 @@ import com.android.systemui.qs.tiles.impl.custom.CustomTileData import com.android.systemui.qs.tiles.impl.custom.CustomTileInteractor import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository +import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent import dagger.Binds import dagger.Module @@ -43,4 +45,9 @@ interface CustomTileModule { @Binds fun bindMapper(customTileMapper: CustomTileMapper): QSTileDataToStateMapper<CustomTileData> + + @Binds + fun bindCustomTileDefaultsRepository( + impl: CustomTileDefaultsRepositoryImpl + ): CustomTileDefaultsRepository } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index cc59f8750b56..dfe6adc49f9a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1070,7 +1070,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( mOnEmptySpaceClickListener); mQsController.init(); - mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged); mShadeHeadsUpTracker.addTrackingHeadsUpListener( mNotificationStackScrollLayoutController::setTrackingHeadsUp); if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) { @@ -4219,7 +4218,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return mShadeExpansionStateManager; } - private void onQsExpansionChanged(boolean expanded) { + void onQsExpansionChanged(boolean expanded) { updateExpandedHeightToMaxHeight(); setStatusAccessibilityImportance(expanded ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 04263882d3a8..51148263b6ac 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -156,7 +156,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW KeyguardStateController keyguardStateController, ScreenOffAnimationController screenOffAnimationController, AuthController authController, - ShadeExpansionStateManager shadeExpansionStateManager, Lazy<ShadeInteractor> shadeInteractorLazy, ShadeWindowLogger logger, Lazy<SelectedUserInteractor> userInteractor) { @@ -185,7 +184,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER); configurationController.addCallback(this); - shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged); float desiredPreferredRefreshRate = context.getResources() .getInteger(R.integer.config_keyguardRefreshRate); @@ -303,6 +301,11 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mShadeInteractorLazy.get().isAnyExpanded(), this::onShadeOrQsExpanded ); + collectFlow( + mWindowRootView, + mShadeInteractorLazy.get().isQsExpanded(), + this::onQsExpansionChanged + ); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index cc46b23ec75f..866cfb443b0a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -26,23 +26,27 @@ import androidx.constraintlayout.widget.ConstraintSet.END import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP -import com.android.systemui.res.R +import androidx.lifecycle.lifecycleScope import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.fragments.FragmentService +import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.qs.QSContainerController import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener +import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.system.QuickStepContract import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.policy.SplitShadeStateController import com.android.systemui.util.LargeScreenUtils import com.android.systemui.util.ViewController import com.android.systemui.util.concurrency.DelayableExecutor +import kotlinx.coroutines.launch import java.util.function.Consumer import javax.inject.Inject import kotlin.reflect.KMutableProperty0 @@ -56,7 +60,7 @@ class NotificationsQSContainerController @Inject constructor( private val navigationModeController: NavigationModeController, private val overviewProxyService: OverviewProxyService, private val shadeHeaderController: ShadeHeaderController, - private val shadeExpansionStateManager: ShadeExpansionStateManager, + private val shadeInteractor: ShadeInteractor, private val fragmentService: FragmentService, @Main private val delayableExecutor: DelayableExecutor, private val featureFlags: FeatureFlags, @@ -65,7 +69,6 @@ class NotificationsQSContainerController @Inject constructor( private val splitShadeStateController: SplitShadeStateController ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController { - private var qsExpanded = false private var splitShadeEnabled = false private var isQSDetailShowing = false private var isQSCustomizing = false @@ -89,13 +92,6 @@ class NotificationsQSContainerController @Inject constructor( taskbarVisible = visible } } - private val shadeQsExpansionListener: ShadeQsExpansionListener = - ShadeQsExpansionListener { isQsExpanded -> - if (qsExpanded != isQsExpanded) { - qsExpanded = isQsExpanded - mView.invalidate() - } - } // With certain configuration changes (like light/dark changes), the nav bar will disappear // for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value @@ -122,6 +118,11 @@ class NotificationsQSContainerController @Inject constructor( } override fun onInit() { + mView.repeatWhenAttached { + lifecycleScope.launch { + shadeInteractor.isQsExpanded.collect{ _ -> mView.invalidate() } + } + } val currentMode: Int = navigationModeController.addListener { mode: Int -> isGestureNavigation = QuickStepContract.isGesturalMode(mode) } @@ -137,7 +138,6 @@ class NotificationsQSContainerController @Inject constructor( public override fun onViewAttached() { updateResources() overviewProxyService.addCallback(taskbarVisibilityListener) - shadeExpansionStateManager.addQsExpansionListener(shadeQsExpansionListener) mView.setInsetsChangedListener(delayedInsetSetter) mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) } mView.setConfigurationChangedListener { updateResources() } @@ -146,7 +146,6 @@ class NotificationsQSContainerController @Inject constructor( override fun onViewDetached() { overviewProxyService.removeCallback(taskbarVisibilityListener) - shadeExpansionStateManager.removeQsExpansionListener(shadeQsExpansionListener) mView.removeOnInsetsChangedListener() mView.removeQSFragmentAttachedListener() mView.setConfigurationChangedListener(null) diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 0ec7a36b5a6d..d73fa1460bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -207,12 +207,6 @@ public class QuickSettingsController implements Dumpable { /** pointerId of the pointer we're currently tracking */ private int mTrackingPointer; - /** - * Indicates that QS is in expanded state which can happen by: - * - single pane shade: expanding shade and then expanding QS - * - split shade: just expanding shade (QS are expanded automatically) - */ - private boolean mExpanded; /** Indicates QS is at its max height */ private boolean mFullyExpanded; /** @@ -594,9 +588,8 @@ public class QuickSettingsController implements Dumpable { return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag; } - public boolean getExpanded() { - return mExpanded; + return mShadeRepository.getLegacyIsQsExpanded().getValue(); } @VisibleForTesting @@ -613,7 +606,7 @@ public class QuickSettingsController implements Dumpable { // close the whole shade with one motion. Also this will be always true when closing // split shade as there QS are always expanded so every collapsing motion is motion from // expanded QS to closed panel - return mExpandImmediate || (mExpanded + return mExpandImmediate || (getExpanded() && !isTracking() && !isExpansionAnimating() && !mExpansionFromOverscroll); } @@ -778,11 +771,11 @@ public class QuickSettingsController implements Dumpable { @VisibleForTesting void setExpanded(boolean expanded) { - boolean changed = mExpanded != expanded; + boolean changed = getExpanded() != expanded; if (changed) { - mExpanded = expanded; + mShadeRepository.setLegacyIsQsExpanded(expanded); updateQsState(); - mShadeExpansionStateManager.onQsExpansionChanged(expanded); + mPanelViewControllerLazy.get().onQsExpansionChanged(expanded); mShadeLog.logQsExpansionChanged("QS Expansion Changed.", expanded, getMinExpansionHeight(), getMaxExpansionHeight(), mStackScrollerOverscrolling, mAnimatorExpand, mAnimating); @@ -846,7 +839,7 @@ public class QuickSettingsController implements Dumpable { /** Called when Shade view layout changed. Updates QS expansion or * starts size change animation if height has changed. */ void handleShadeLayoutChanged(int oldMaxHeight) { - if (mExpanded && mFullyExpanded) { + if (getExpanded() && mFullyExpanded) { mExpansionHeight = mMaxExpansionHeight; if (mExpansionHeightSetToMaxListener != null) { mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true); @@ -988,24 +981,24 @@ public class QuickSettingsController implements Dumpable { } void updateQsState() { - boolean qsFullScreen = mExpanded && !mSplitShadeEnabled; + boolean qsFullScreen = getExpanded() && !mSplitShadeEnabled; mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen); mNotificationStackScrollLayoutController.setScrollingEnabled( mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll)); if (mQsStateUpdateListener != null) { - mQsStateUpdateListener.onQsStateUpdated(mExpanded, mStackScrollerOverscrolling); + mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling); } if (mQs == null) return; - mQs.setExpanded(mExpanded); + mQs.setExpanded(getExpanded()); } /** update expanded state of QS */ public void updateExpansion() { if (mQs == null) return; final float squishiness; - if ((mExpandImmediate || mExpanded) && !mSplitShadeEnabled) { + if ((mExpandImmediate || getExpanded()) && !mSplitShadeEnabled) { squishiness = 1; } else if (mTransitioningToFullShadeProgress > 0.0f) { squishiness = mLockscreenShadeTransitionController.getQsSquishTransitionFraction(); @@ -1125,7 +1118,7 @@ public class QuickSettingsController implements Dumpable { mMediaHierarchyManager.setCollapsingShadeFromQS(mExpandedWhenExpandingStarted /* We also start expanding when flinging closed Qs. Let's exclude that */ && !mAnimating); - if (mExpanded) { + if (getExpanded()) { onExpansionStarted(); } // Since there are QS tiles in the header now, we need to make sure we start listening @@ -1234,7 +1227,7 @@ public class QuickSettingsController implements Dumpable { Math.min(top / (float) mScrimCornerRadius, 1f)); float bottomRadius = mSplitShadeEnabled ? screenCornerRadius : 0; - if (!mExpanded) { + if (!getExpanded()) { bottomRadius = calculateBottomCornerRadius(bottomRadius); } mScrimController.setNotificationBottomRadius(bottomRadius); @@ -1547,7 +1540,7 @@ public class QuickSettingsController implements Dumpable { @VisibleForTesting void onHeightChanged() { mMaxExpansionHeight = isQsFragmentCreated() ? mQs.getDesiredHeight() : 0; - if (mExpanded && mFullyExpanded) { + if (getExpanded() && mFullyExpanded) { mExpansionHeight = mMaxExpansionHeight; if (mExpansionHeightSetToMaxListener != null) { mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true); @@ -2083,7 +2076,7 @@ public class QuickSettingsController implements Dumpable { ipw.print("mTrackingPointer="); ipw.println(mTrackingPointer); ipw.print("mExpanded="); - ipw.println(mExpanded); + ipw.println(getExpanded()); ipw.print("mFullyExpanded="); ipw.println(mFullyExpanded); ipw.print("mExpandImmediate="); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt index 949398969d67..fca98f580702 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt @@ -36,15 +36,12 @@ import javax.inject.Inject class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents { private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>() - private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>() private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>() private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>() @PanelState private var state: Int = STATE_CLOSED @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f private var expanded: Boolean = false - private var qsExpanded: Boolean = false - private var qsExpansionFraction = 0f private var tracking: Boolean = false private var dragDownPxAmount: Float = 0f @@ -64,15 +61,6 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents { expansionListeners.remove(listener) } - fun addQsExpansionListener(listener: ShadeQsExpansionListener) { - qsExpansionListeners.add(listener) - listener.onQsExpansionChanged(qsExpanded) - } - - fun removeQsExpansionListener(listener: ShadeQsExpansionListener) { - qsExpansionListeners.remove(listener) - } - /** Adds a listener that will be notified when the panel state has changed. */ fun addStateListener(listener: ShadeStateListener) { stateListeners.add(listener) @@ -153,14 +141,6 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents { expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) } } - /** Called when the quick settings expansion changes to fully expanded or collapsed. */ - fun onQsExpansionChanged(qsExpanded: Boolean) { - this.qsExpanded = qsExpanded - - debugLog("qsExpanded=$qsExpanded") - qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) } - } - /** Updates the panel state if necessary. */ fun updateState(@PanelState state: Int) { debugLog( diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt index 024c8e32a441..e2e4556f59f7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt @@ -83,6 +83,17 @@ interface ShadeRepository { val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> /** + * QuickSettingsController.mExpanded as a flow. Indicates that QS is in expanded state: + * - single pane shade: expanding shade and then expanding QS + * - split shade: just expanding shade (QS are expanded automatically) + */ + @Deprecated("Use ShadeInteractor instead") val legacyIsQsExpanded: StateFlow<Boolean> + + /** Sets whether QS is expanded. */ + @Deprecated("Use ShadeInteractor instead") + fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) + + /** * Sets whether the expansion fraction is greater than zero or NPVC is about to accept an input * transfer from the status bar, home screen, or trackpad. */ @@ -175,6 +186,15 @@ constructor(shadeExpansionStateManager: ShadeExpansionStateManager) : ShadeRepos override val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> = _legacyExpandedOrAwaitingInputTransfer.asStateFlow() + private val _legacyIsQsExpanded = MutableStateFlow(false) + @Deprecated("Use ShadeInteractor instead") + override val legacyIsQsExpanded: StateFlow<Boolean> = _legacyIsQsExpanded.asStateFlow() + + @Deprecated("Use ShadeInteractor instead") + override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) { + _legacyIsQsExpanded.value = legacyIsQsExpanded + } + @Deprecated("Use ShadeInteractor instead") override fun setLegacyExpandedOrAwaitingInputTransfer( legacyExpandedOrAwaitingInputTransfer: Boolean diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt index f043c717f923..a4c4503a6550 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -121,16 +121,37 @@ constructor( /** * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will - * report 0f. + * report 0f. If split shade is enabled, value matches shadeExpansion. */ val qsExpansion: StateFlow<Float> = if (sceneContainerFlags.isEnabled()) { - sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings) + val qsExp = sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings) + combine(isSplitShadeEnabled, shadeExpansion, qsExp) { + isSplitShadeEnabled, + shadeExp, + qsExp -> + if (isSplitShadeEnabled) { + shadeExp + } else { + qsExp + } + } .stateIn(scope, SharingStarted.Eagerly, 0f) } else { repository.qsExpansion } + /** Whether Quick Settings is expanded a non-zero amount. */ + val isQsExpanded: StateFlow<Boolean> = + if (sceneContainerFlags.isEnabled()) { + qsExpansion + .map { it > 0 } + .distinctUntilChanged() + .stateIn(scope, SharingStarted.Eagerly, false) + } else { + repository.legacyIsQsExpanded + } + /** The amount [0-1] either QS or the shade has been opened. */ val anyExpansion: StateFlow<Float> = combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 4ea70264b152..e19fcd05e9a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -32,17 +32,15 @@ import com.android.app.animation.Interpolators import com.android.systemui.Dumpable import com.android.systemui.Gefingerpoken import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN -import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R -import com.android.systemui.shade.ShadeExpansionStateManager +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView -import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController @@ -64,13 +62,11 @@ constructor( private val wakeUpCoordinator: NotificationWakeUpCoordinator, private val bypassController: KeyguardBypassController, private val headsUpManager: HeadsUpManager, - private val roundnessManager: NotificationRoundnessManager, configurationController: ConfigurationController, private val statusBarStateController: StatusBarStateController, private val falsingManager: FalsingManager, - shadeExpansionStateManager: ShadeExpansionStateManager, + private val shadeInteractor: ShadeInteractor, private val lockscreenShadeTransitionController: LockscreenShadeTransitionController, - private val falsingCollector: FalsingCollector, dumpManager: DumpManager ) : Gefingerpoken, Dumpable { companion object { @@ -115,7 +111,6 @@ constructor( private val isFalseTouch: Boolean get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) - var qsExpanded: Boolean = false var pulseExpandAbortListener: Runnable? = null var bouncerShowing: Boolean = false @@ -127,12 +122,6 @@ constructor( } }) - shadeExpansionStateManager.addQsExpansionListener { isQsExpanded -> - if (qsExpanded != isQsExpanded) { - qsExpanded = isQsExpanded - } - } - mPowerManager = context.getSystemService(PowerManager::class.java) dumpManager.registerDumpable(this) } @@ -148,7 +137,8 @@ constructor( } private fun canHandleMotionEvent(): Boolean { - return wakeUpCoordinator.canShowPulsingHuns && !qsExpanded && !bouncerShowing + return wakeUpCoordinator.canShowPulsingHuns && !shadeInteractor.isQsExpanded.value && + !bouncerShowing } private fun startExpansion(event: MotionEvent): Boolean { @@ -379,7 +369,6 @@ constructor( it.println("isExpanding: $isExpanding") it.println("leavingLockscreen: $leavingLockscreen") it.println("mPulsing: $mPulsing") - it.println("qsExpanded: $qsExpanded") it.println("bouncerShowing: $bouncerShowing") } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt index 5af7cfc212d5..a36d36c3827b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.events -import androidx.core.animation.Animator import android.annotation.UiThread import android.graphics.Point import android.graphics.Rect @@ -24,13 +23,15 @@ import android.util.Log import android.view.Gravity import android.view.View import android.widget.FrameLayout -import com.android.internal.annotations.GuardedBy -import com.android.systemui.res.R +import androidx.core.animation.Animator import com.android.app.animation.Interpolators +import com.android.internal.annotations.GuardedBy import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.ShadeExpansionStateManager +import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.StatusBarState.SHADE import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener @@ -43,6 +44,8 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import java.util.concurrent.Executor import javax.inject.Inject @@ -64,11 +67,12 @@ import javax.inject.Inject @SysUISingleton open class PrivacyDotViewController @Inject constructor( @Main private val mainExecutor: Executor, + @Application scope: CoroutineScope, private val stateController: StatusBarStateController, private val configurationController: ConfigurationController, private val contentInsetsProvider: StatusBarContentInsetsProvider, private val animationScheduler: SystemStatusAnimationScheduler, - shadeExpansionStateManager: ShadeExpansionStateManager + shadeInteractor: ShadeInteractor? ) { private lateinit var tl: View private lateinit var tr: View @@ -135,10 +139,12 @@ open class PrivacyDotViewController @Inject constructor( } }) - shadeExpansionStateManager.addQsExpansionListener { isQsExpanded -> - dlog("setQsExpanded $isQsExpanded") - synchronized(lock) { - nextViewState = nextViewState.copy(qsExpanded = isQsExpanded) + scope.launch { + shadeInteractor?.isQsExpanded?.collect { isQsExpanded -> + dlog("setQsExpanded $isQsExpanded") + synchronized(lock) { + nextViewState = nextViewState.copy(qsExpanded = isQsExpanded) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt index 84796f9acbc0..ed964821a5c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt @@ -17,19 +17,11 @@ package com.android.systemui.statusbar.events import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.statusbar.window.StatusBarWindowController -import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.time.SystemClock +import dagger.Binds import dagger.Module import dagger.Provides -import kotlinx.coroutines.CoroutineScope @Module interface StatusBarEventsModule { @@ -42,41 +34,11 @@ interface StatusBarEventsModule { fun provideSystemStatusAnimationSchedulerLogBuffer(factory: LogBufferFactory): LogBuffer { return factory.create("SystemStatusAnimationSchedulerLog", 60) } - - @Provides - @SysUISingleton - fun provideSystemStatusAnimationScheduler( - featureFlags: FeatureFlags, - coordinator: SystemEventCoordinator, - chipAnimationController: SystemEventChipAnimationController, - statusBarWindowController: StatusBarWindowController, - dumpManager: DumpManager, - systemClock: SystemClock, - @Application coroutineScope: CoroutineScope, - @Main executor: DelayableExecutor, - logger: SystemStatusAnimationSchedulerLogger - ): SystemStatusAnimationScheduler { - return if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) { - SystemStatusAnimationSchedulerImpl( - coordinator, - chipAnimationController, - statusBarWindowController, - dumpManager, - systemClock, - coroutineScope, - logger - ) - } else { - SystemStatusAnimationSchedulerLegacyImpl( - coordinator, - chipAnimationController, - statusBarWindowController, - dumpManager, - systemClock, - executor - ) - } - } } -} + @Binds + @SysUISingleton + fun bindSystemStatusAnimationScheduler( + systemStatusAnimationSchedulerImpl: SystemStatusAnimationSchedulerImpl + ): SystemStatusAnimationScheduler +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 73c0bfec69a1..fec176523b4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -32,8 +32,6 @@ import androidx.core.animation.AnimatorSet import androidx.core.animation.ValueAnimator import com.android.internal.annotations.VisibleForTesting import com.android.systemui.res.R -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController @@ -47,8 +45,7 @@ import kotlin.math.roundToInt class SystemEventChipAnimationController @Inject constructor( private val context: Context, private val statusBarWindowController: StatusBarWindowController, - private val contentInsetsProvider: StatusBarContentInsetsProvider, - private val featureFlags: FeatureFlags, + private val contentInsetsProvider: StatusBarContentInsetsProvider ) : SystemStatusAnimationCallback { private lateinit var animationWindowView: FrameLayout @@ -317,15 +314,8 @@ class SystemEventChipAnimationController @Inject constructor( it.marginEnd = marginEnd } - private fun initializeAnimRect() = if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) { - animRect.set(chipBounds) - } else { - animRect.set( - chipLeft, - currentAnimatedView!!.view.top, - chipRight, - currentAnimatedView!!.view.bottom) - } + private fun initializeAnimRect() = animRect.set(chipBounds) + /** * To be called during an animation, sets the width and updates the current animated chip view diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt index e9d5deccac94..a73d517a00e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt @@ -24,8 +24,7 @@ import com.android.systemui.res.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags +import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State import com.android.systemui.privacy.PrivacyChipBuilder import com.android.systemui.privacy.PrivacyItem import com.android.systemui.privacy.PrivacyItemController @@ -49,7 +48,6 @@ constructor( private val batteryController: BatteryController, private val privacyController: PrivacyItemController, private val context: Context, - private val featureFlags: FeatureFlags, @Application private val appScope: CoroutineScope, connectedDisplayInteractor: ConnectedDisplayInteractor ) { @@ -76,9 +74,7 @@ constructor( } fun notifyPluggedIn(@IntRange(from = 0, to = 100) batteryLevel: Int) { - if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) { - scheduler.onStatusEvent(BatteryEvent(batteryLevel)) - } + scheduler.onStatusEvent(BatteryEvent(batteryLevel)) } fun notifyPrivacyItemsEmpty() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt deleted file mode 100644 index 6b5a548b0afe..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.events - -import android.os.Process -import android.provider.DeviceConfig -import android.util.Log -import androidx.core.animation.Animator -import androidx.core.animation.AnimatorListenerAdapter -import androidx.core.animation.AnimatorSet -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dump.DumpManager -import com.android.systemui.statusbar.window.StatusBarWindowController -import com.android.systemui.util.Assert -import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.time.SystemClock -import java.io.PrintWriter -import javax.inject.Inject - -/** - * Dead-simple scheduler for system status events. Obeys the following principles (all values TBD): - * ``` - * - Avoiding log spam by only allowing 12 events per minute (1event/5s) - * - Waits 100ms to schedule any event for debouncing/prioritization - * - Simple prioritization: Privacy > Battery > connectivity (encoded in [StatusEvent]) - * - Only schedules a single event, and throws away lowest priority events - * ``` - * - * There are 4 basic stages of animation at play here: - * ``` - * 1. System chrome animation OUT - * 2. Chip animation IN - * 3. Chip animation OUT; potentially into a dot - * 4. System chrome animation IN - * ``` - * - * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system - * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize - * their respective views based on the progress of the animator. Interpolation differences TBD - */ -open class SystemStatusAnimationSchedulerLegacyImpl -@Inject -constructor( - private val coordinator: SystemEventCoordinator, - private val chipAnimationController: SystemEventChipAnimationController, - private val statusBarWindowController: StatusBarWindowController, - private val dumpManager: DumpManager, - private val systemClock: SystemClock, - @Main private val executor: DelayableExecutor -) : SystemStatusAnimationScheduler { - - companion object { - private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator" - } - - fun isImmersiveIndicatorEnabled(): Boolean { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_ENABLE_IMMERSIVE_INDICATOR, - true - ) - } - - @SystemAnimationState private var animationState: Int = IDLE - - /** True if the persistent privacy dot should be active */ - var hasPersistentDot = false - protected set - - private var scheduledEvent: StatusEvent? = null - - val listeners = mutableSetOf<SystemStatusAnimationCallback>() - - init { - coordinator.attachScheduler(this) - dumpManager.registerDumpable(TAG, this) - } - - @SystemAnimationState override fun getAnimationState() = animationState - - override fun onStatusEvent(event: StatusEvent) { - // Ignore any updates until the system is up and running. However, for important events that - // request to be force visible (like privacy), ignore whether it's too early. - if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) { - return - } - - // Don't deal with threading for now (no need let's be honest) - Assert.isMainThread() - if ( - (event.priority > (scheduledEvent?.priority ?: -1)) && - animationState != ANIMATING_OUT && - animationState != SHOWING_PERSISTENT_DOT - ) { - // events can only be scheduled if a higher priority or no other event is in progress - if (DEBUG) { - Log.d(TAG, "scheduling event $event") - } - - scheduleEvent(event) - } else if (scheduledEvent?.shouldUpdateFromEvent(event) == true) { - if (DEBUG) { - Log.d(TAG, "updating current event from: $event. animationState=$animationState") - } - scheduledEvent?.updateFromEvent(event) - if (event.forceVisible) { - hasPersistentDot = true - // If we missed the chance to show the persistent dot, do it now - if (animationState == IDLE) { - notifyTransitionToPersistentDot() - } - } - } else { - if (DEBUG) { - Log.d(TAG, "ignoring event $event") - } - } - } - - override fun removePersistentDot() { - if (!hasPersistentDot || !isImmersiveIndicatorEnabled()) { - return - } - - hasPersistentDot = false - notifyHidePersistentDot() - return - } - - fun isTooEarly(): Boolean { - return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME - } - - /** Clear the scheduled event (if any) and schedule a new one */ - private fun scheduleEvent(event: StatusEvent) { - scheduledEvent = event - - if (event.forceVisible) { - hasPersistentDot = true - } - - // If animations are turned off, we'll transition directly to the dot - if (!event.showAnimation && event.forceVisible) { - notifyTransitionToPersistentDot() - scheduledEvent = null - return - } - - chipAnimationController.prepareChipAnimation(scheduledEvent!!.viewCreator) - animationState = ANIMATION_QUEUED - executor.executeDelayed({ runChipAnimation() }, DEBOUNCE_DELAY) - } - - /** - * 1. Define a total budget for the chip animation (1500ms) - * 2. Send out callbacks to listeners so that they can generate animations locally - * 3. Update the scheduler state so that clients know where we are - * 4. Maybe: provide scaffolding such as: dot location, margins, etc - * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we - * collect all of the animators and run them together. - */ - private fun runChipAnimation() { - statusBarWindowController.setForceStatusBarVisible(true) - animationState = ANIMATING_IN - - val animSet = collectStartAnimations() - if (animSet.totalDuration > 500) { - throw IllegalStateException( - "System animation total length exceeds budget. " + - "Expected: 500, actual: ${animSet.totalDuration}" - ) - } - animSet.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - animationState = RUNNING_CHIP_ANIM - } - } - ) - animSet.start() - - executor.executeDelayed( - { - val animSet2 = collectFinishAnimations() - animationState = ANIMATING_OUT - animSet2.addListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - animationState = - if (hasPersistentDot) { - SHOWING_PERSISTENT_DOT - } else { - IDLE - } - - statusBarWindowController.setForceStatusBarVisible(false) - } - } - ) - animSet2.start() - scheduledEvent = null - }, - DISPLAY_LENGTH - ) - } - - private fun collectStartAnimations(): AnimatorSet { - val animators = mutableListOf<Animator>() - listeners.forEach { listener -> - listener.onSystemEventAnimationBegin()?.let { anim -> animators.add(anim) } - } - animators.add(chipAnimationController.onSystemEventAnimationBegin()) - val animSet = AnimatorSet().also { it.playTogether(animators) } - - return animSet - } - - private fun collectFinishAnimations(): AnimatorSet { - val animators = mutableListOf<Animator>() - listeners.forEach { listener -> - listener.onSystemEventAnimationFinish(hasPersistentDot)?.let { anim -> - animators.add(anim) - } - } - animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot)) - if (hasPersistentDot) { - val dotAnim = notifyTransitionToPersistentDot() - if (dotAnim != null) { - animators.add(dotAnim) - } - } - val animSet = AnimatorSet().also { it.playTogether(animators) } - - return animSet - } - - private fun notifyTransitionToPersistentDot(): Animator? { - val anims: List<Animator> = - listeners.mapNotNull { - it.onSystemStatusAnimationTransitionToPersistentDot( - scheduledEvent?.contentDescription - ) - } - if (anims.isNotEmpty()) { - val aSet = AnimatorSet() - aSet.playTogether(anims) - return aSet - } - - return null - } - - private fun notifyHidePersistentDot(): Animator? { - val anims: List<Animator> = listeners.mapNotNull { it.onHidePersistentDot() } - - if (animationState == SHOWING_PERSISTENT_DOT) { - animationState = IDLE - } - - if (anims.isNotEmpty()) { - val aSet = AnimatorSet() - aSet.playTogether(anims) - return aSet - } - - return null - } - - override fun addCallback(listener: SystemStatusAnimationCallback) { - Assert.isMainThread() - - if (listeners.isEmpty()) { - coordinator.startObserving() - } - listeners.add(listener) - } - - override fun removeCallback(listener: SystemStatusAnimationCallback) { - Assert.isMainThread() - - listeners.remove(listener) - if (listeners.isEmpty()) { - coordinator.stopObserving() - } - } - - override fun dump(pw: PrintWriter, args: Array<out String>) { - pw.println("Scheduled event: $scheduledEvent") - pw.println("Has persistent privacy dot: $hasPersistentDot") - pw.println("Animation state: $animationState") - pw.println("Listeners:") - if (listeners.isEmpty()) { - pw.println("(none)") - } else { - listeners.forEach { pw.println(" $it") } - } - } -} - -private const val DEBUG = false -private const val TAG = "SystemStatusAnimationSchedulerLegacyImpl" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt new file mode 100644 index 000000000000..11c982590477 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.interruption + +import android.app.Notification.VISIBILITY_PRIVATE +import android.app.NotificationManager.IMPORTANCE_DEFAULT +import android.app.NotificationManager.IMPORTANCE_HIGH +import android.database.ContentObserver +import android.hardware.display.AmbientDisplayConfiguration +import android.os.Handler +import android.os.PowerManager +import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED +import android.provider.Settings.Global.HEADS_UP_OFF +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.util.settings.GlobalSettings +import com.android.systemui.util.time.SystemClock + +class PeekDisabledSuppressor( + private val globalSettings: GlobalSettings, + private val headsUpManager: HeadsUpManager, + private val logger: NotificationInterruptLogger, + @Main private val mainHandler: Handler, +) : VisualInterruptionCondition(types = setOf(PEEK), reason = "peek setting disabled") { + private var isEnabled = false + + override fun shouldSuppress(): Boolean = !isEnabled + + override fun start() { + val observer = + object : ContentObserver(mainHandler) { + override fun onChange(selfChange: Boolean) { + val wasEnabled = isEnabled + + isEnabled = + globalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF) != + HEADS_UP_OFF + + // QQQ: Do we want to log this even if it hasn't changed? + logger.logHeadsUpFeatureChanged(isEnabled) + + // QQQ: Is there a better place for this side effect? What if HeadsUpManager + // registered for it directly? + if (wasEnabled && !isEnabled) { + logger.logWillDismissAll() + headsUpManager.releaseAllImmediately() + } + } + } + + globalSettings.registerContentObserver( + globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED), + /* notifyForDescendants = */ true, + observer + ) + + // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused. + + observer.onChange(/* selfChange = */ true) + } +} + +class PulseDisabledSuppressor( + private val ambientDisplayConfiguration: AmbientDisplayConfiguration, + private val userTracker: UserTracker, +) : VisualInterruptionCondition(types = setOf(PULSE), reason = "pulse setting disabled") { + override fun shouldSuppress(): Boolean = + !ambientDisplayConfiguration.pulseOnNotificationEnabled(userTracker.userId) +} + +class PulseBatterySaverSuppressor(private val batteryController: BatteryController) : + VisualInterruptionCondition( + types = setOf(PULSE), + reason = "pulsing disabled by battery saver" + ) { + override fun shouldSuppress() = batteryController.isAodPowerSave() +} + +class PeekPackageSnoozedSuppressor(private val headsUpManager: HeadsUpManager) : + VisualInterruptionFilter(types = setOf(PEEK), reason = "package snoozed") { + override fun shouldSuppress(entry: NotificationEntry) = + when { + // Assume any notification with an FSI is time-sensitive (like an alarm or incoming + // call) and ignore whether HUNs have been snoozed for the package. + entry.sbn.notification.fullScreenIntent != null -> false + + // Otherwise, check if the package is snoozed. + else -> headsUpManager.isSnoozed(entry.sbn.packageName) + } +} + +class PeekAlreadyBubbledSuppressor(private val statusBarStateController: StatusBarStateController) : + VisualInterruptionFilter(types = setOf(PEEK), reason = "already bubbled") { + override fun shouldSuppress(entry: NotificationEntry) = + when { + statusBarStateController.state != SHADE -> false + else -> entry.isBubble + } +} + +class PeekDndSuppressor() : + VisualInterruptionFilter(types = setOf(PEEK), reason = "suppressed by DND") { + override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressPeek() +} + +class PeekNotImportantSuppressor() : + VisualInterruptionFilter(types = setOf(PEEK), reason = "not important") { + override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_HIGH +} + +class PeekDeviceNotInUseSuppressor( + private val powerManager: PowerManager, + private val statusBarStateController: StatusBarStateController +) : VisualInterruptionCondition(types = setOf(PEEK), reason = "not in use") { + override fun shouldSuppress() = + when { + !powerManager.isScreenOn || statusBarStateController.isDreaming -> true + else -> false + } +} + +class PeekOldWhenSuppressor(private val systemClock: SystemClock) : + VisualInterruptionFilter(types = setOf(PEEK), reason = "old when") { + private fun whenAge(entry: NotificationEntry) = + systemClock.currentTimeMillis() - entry.sbn.notification.`when` + + override fun shouldSuppress(entry: NotificationEntry): Boolean = + when { + // Ignore a "when" of 0, as it is unlikely to be a meaningful timestamp. + entry.sbn.notification.`when` <= 0L -> false + + // Assume all HUNs with FSIs, foreground services, or user-initiated jobs are + // time-sensitive, regardless of their "when". + entry.sbn.notification.fullScreenIntent != null || + entry.sbn.notification.isForegroundService || + entry.sbn.notification.isUserInitiatedJob -> false + + // Otherwise, check if the HUN's "when" is too old. + else -> whenAge(entry) >= MAX_HUN_WHEN_AGE_MS + } +} + +class PulseEffectSuppressor() : + VisualInterruptionFilter(types = setOf(PULSE), reason = "ambient effect suppressed") { + override fun shouldSuppress(entry: NotificationEntry) = entry.shouldSuppressAmbient() +} + +class PulseLockscreenVisibilityPrivateSuppressor() : + VisualInterruptionFilter( + types = setOf(PULSE), + reason = "notification hidden on lock screen by override" + ) { + override fun shouldSuppress(entry: NotificationEntry) = + entry.ranking.lockscreenVisibilityOverride == VISIBILITY_PRIVATE +} + +class PulseLowImportanceSuppressor() : + VisualInterruptionFilter(types = setOf(PULSE), reason = "importance less than DEFAULT") { + override fun shouldSuppress(entry: NotificationEntry) = entry.importance < IMPORTANCE_DEFAULT +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 301ddbf42ff2..f2ade340fc4f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.notification.interruption; +import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; +import static android.provider.Settings.Global.HEADS_UP_OFF; + import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD; import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA; @@ -24,12 +27,10 @@ import static com.android.systemui.statusbar.notification.interruption.Notificat import android.app.Notification; import android.app.NotificationManager; -import android.content.ContentResolver; import android.database.ContentObserver; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.PowerManager; -import android.provider.Settings; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; @@ -48,6 +49,8 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.SystemClock; import java.util.ArrayList; import java.util.List; @@ -66,7 +69,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>(); private final StatusBarStateController mStatusBarStateController; private final KeyguardStateController mKeyguardStateController; - private final ContentResolver mContentResolver; private final PowerManager mPowerManager; private final AmbientDisplayConfiguration mAmbientDisplayConfiguration; private final BatteryController mBatteryController; @@ -77,6 +79,8 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter private final UiEventLogger mUiEventLogger; private final UserTracker mUserTracker; private final DeviceProvisionedController mDeviceProvisionedController; + private final SystemClock mSystemClock; + private final GlobalSettings mGlobalSettings; @VisibleForTesting protected boolean mUseHeadsUp = false; @@ -111,7 +115,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter @Inject public NotificationInterruptStateProviderImpl( - ContentResolver contentResolver, PowerManager powerManager, AmbientDisplayConfiguration ambientDisplayConfiguration, BatteryController batteryController, @@ -124,8 +127,9 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, UiEventLogger uiEventLogger, UserTracker userTracker, - DeviceProvisionedController deviceProvisionedController) { - mContentResolver = contentResolver; + DeviceProvisionedController deviceProvisionedController, + SystemClock systemClock, + GlobalSettings globalSettings) { mPowerManager = powerManager; mBatteryController = batteryController; mAmbientDisplayConfiguration = ambientDisplayConfiguration; @@ -137,15 +141,16 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter mKeyguardNotificationVisibilityProvider = keyguardNotificationVisibilityProvider; mUiEventLogger = uiEventLogger; mUserTracker = userTracker; + mDeviceProvisionedController = deviceProvisionedController; + mSystemClock = systemClock; + mGlobalSettings = globalSettings; ContentObserver headsUpObserver = new ContentObserver(mainHandler) { @Override public void onChange(boolean selfChange) { - boolean wasUsing = mUseHeadsUp; - mUseHeadsUp = ENABLE_HEADS_UP - && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( - mContentResolver, - Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, - Settings.Global.HEADS_UP_OFF); + final boolean wasUsing = mUseHeadsUp; + final boolean settingEnabled = HEADS_UP_OFF + != mGlobalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF); + mUseHeadsUp = ENABLE_HEADS_UP && settingEnabled; mLogger.logHeadsUpFeatureChanged(mUseHeadsUp); if (wasUsing != mUseHeadsUp) { if (!mUseHeadsUp) { @@ -157,16 +162,15 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter }; if (ENABLE_HEADS_UP) { - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), + mGlobalSettings.registerContentObserver( + mGlobalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED), true, headsUpObserver); - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, + mGlobalSettings.registerContentObserver( + mGlobalSettings.getUriFor(SETTING_HEADS_UP_TICKER), true, headsUpObserver); } headsUpObserver.onChange(true); // set up - mDeviceProvisionedController = deviceProvisionedController; } @Override @@ -603,7 +607,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } final long when = notification.when; - final long now = System.currentTimeMillis(); + final long now = mSystemClock.currentTimeMillis(); final long age = now - when; if (age < MAX_HUN_WHEN_AGE_MS) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt index 920bbe96b33b..da8474e92629 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.systemui.statusbar.notification.interruption import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -51,6 +52,13 @@ interface VisualInterruptionDecisionProvider { } /** + * Initializes the provider. + * + * Must be called before any method except [addLegacySuppressor]. + */ + fun start() {} + + /** * Adds a [component][suppressor] that can suppress visual interruptions. * * This class may call suppressors in any order. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt new file mode 100644 index 000000000000..7f144bf14615 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.notification.interruption + +import android.hardware.display.AmbientDisplayConfiguration +import android.os.Handler +import android.os.PowerManager +import android.util.Log +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.FullScreenIntentDecision +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.util.settings.GlobalSettings +import com.android.systemui.util.time.SystemClock +import javax.inject.Inject + +class VisualInterruptionDecisionProviderImpl +@Inject +constructor( + private val ambientDisplayConfiguration: AmbientDisplayConfiguration, + private val batteryController: BatteryController, + private val globalSettings: GlobalSettings, + private val headsUpManager: HeadsUpManager, + private val logger: NotificationInterruptLogger, + @Main private val mainHandler: Handler, + private val powerManager: PowerManager, + private val statusBarStateController: StatusBarStateController, + private val systemClock: SystemClock, + private val userTracker: UserTracker, +) : VisualInterruptionDecisionProvider { + private var started = false + + override fun start() { + check(!started) + + addCondition(PeekDisabledSuppressor(globalSettings, headsUpManager, logger, mainHandler)) + addCondition(PulseDisabledSuppressor(ambientDisplayConfiguration, userTracker)) + addCondition(PulseBatterySaverSuppressor(batteryController)) + addFilter(PeekPackageSnoozedSuppressor(headsUpManager)) + addFilter(PeekAlreadyBubbledSuppressor(statusBarStateController)) + addFilter(PeekDndSuppressor()) + addFilter(PeekNotImportantSuppressor()) + addCondition(PeekDeviceNotInUseSuppressor(powerManager, statusBarStateController)) + addFilter(PeekOldWhenSuppressor(systemClock)) + addFilter(PulseEffectSuppressor()) + addFilter(PulseLockscreenVisibilityPrivateSuppressor()) + addFilter(PulseLowImportanceSuppressor()) + + started = true + } + + private class DecisionImpl( + override val shouldInterrupt: Boolean, + override val logReason: String + ) : Decision + + private class FullScreenIntentDecisionImpl( + override val shouldInterrupt: Boolean, + override val wouldInterruptWithoutDnd: Boolean, + override val logReason: String, + val originalEntry: NotificationEntry, + ) : FullScreenIntentDecision { + var hasBeenLogged = false + } + + private val legacySuppressors = mutableSetOf<NotificationInterruptSuppressor>() + private val conditions = mutableListOf<VisualInterruptionCondition>() + private val filters = mutableListOf<VisualInterruptionFilter>() + + override fun addLegacySuppressor(suppressor: NotificationInterruptSuppressor) { + legacySuppressors.add(suppressor) + } + + override fun removeLegacySuppressor(suppressor: NotificationInterruptSuppressor) { + legacySuppressors.remove(suppressor) + } + + fun addCondition(condition: VisualInterruptionCondition) { + conditions.add(condition) + condition.start() + } + + fun addFilter(filter: VisualInterruptionFilter) { + filters.add(filter) + filter.start() + } + + override fun makeUnloggedHeadsUpDecision(entry: NotificationEntry): Decision { + check(started) + return makeHeadsUpDecision(entry) + } + + override fun makeAndLogHeadsUpDecision(entry: NotificationEntry): Decision { + check(started) + return makeHeadsUpDecision(entry).also { logHeadsUpDecision(entry, it) } + } + + override fun makeUnloggedFullScreenIntentDecision( + entry: NotificationEntry + ): FullScreenIntentDecision { + check(started) + return makeFullScreenDecision(entry) + } + + override fun logFullScreenIntentDecision(decision: FullScreenIntentDecision) { + check(started) + val decisionImpl = + decision as? FullScreenIntentDecisionImpl + ?: run { + Log.wtf(TAG, "Wrong subclass of FullScreenIntentDecision: $decision") + return + } + if (decision.hasBeenLogged) { + Log.wtf(TAG, "Already logged decision: $decision") + return + } + logFullScreenIntentDecision(decisionImpl) + decision.hasBeenLogged = true + } + + override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision { + check(started) + return makeBubbleDecision(entry).also { logBubbleDecision(entry, it) } + } + + private fun makeHeadsUpDecision(entry: NotificationEntry): DecisionImpl { + if (statusBarStateController.isDozing) { + return makePulseDecision(entry) + } else { + return makePeekDecision(entry) + } + } + + private fun makePeekDecision(entry: NotificationEntry): DecisionImpl { + checkConditions(PEEK)?.let { + return DecisionImpl(shouldInterrupt = false, logReason = it.reason) + } + checkFilters(PEEK, entry)?.let { + return DecisionImpl(shouldInterrupt = false, logReason = it.reason) + } + checkSuppressors(entry)?.let { + return DecisionImpl( + shouldInterrupt = false, + logReason = "${it.name}.suppressInterruptions" + ) + } + checkAwakeSuppressors(entry)?.let { + return DecisionImpl( + shouldInterrupt = false, + logReason = "${it.name}.suppressAwakeInterruptions" + ) + } + checkAwakeHeadsUpSuppressors(entry)?.let { + return DecisionImpl( + shouldInterrupt = false, + logReason = "${it.name}.suppressAwakeHeadsUpInterruptions" + ) + } + return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed") + } + + private fun makePulseDecision(entry: NotificationEntry): DecisionImpl { + checkConditions(PULSE)?.let { + return DecisionImpl(shouldInterrupt = false, logReason = it.reason) + } + checkFilters(PULSE, entry)?.let { + return DecisionImpl(shouldInterrupt = false, logReason = it.reason) + } + checkSuppressors(entry)?.let { + return DecisionImpl( + shouldInterrupt = false, + logReason = "${it.name}.suppressInterruptions" + ) + } + return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed") + } + + private fun makeBubbleDecision(entry: NotificationEntry): DecisionImpl { + checkConditions(BUBBLE)?.let { + return DecisionImpl(shouldInterrupt = false, logReason = it.reason) + } + checkFilters(BUBBLE, entry)?.let { + return DecisionImpl(shouldInterrupt = false, logReason = it.reason) + } + checkSuppressors(entry)?.let { + return DecisionImpl( + shouldInterrupt = false, + logReason = "${it.name}.suppressInterruptions" + ) + } + checkAwakeSuppressors(entry)?.let { + return DecisionImpl( + shouldInterrupt = false, + logReason = "${it.name}.suppressAwakeInterruptions" + ) + } + return DecisionImpl(shouldInterrupt = true, logReason = "not suppressed") + } + + private fun makeFullScreenDecision(entry: NotificationEntry): FullScreenIntentDecisionImpl { + // Not yet implemented. + return FullScreenIntentDecisionImpl( + shouldInterrupt = true, + wouldInterruptWithoutDnd = true, + logReason = "FSI logic not yet implemented in VisualInterruptionDecisionProviderImpl", + originalEntry = entry + ) + } + + private fun logHeadsUpDecision(entry: NotificationEntry, decision: DecisionImpl) { + // Not yet implemented. + } + + private fun logBubbleDecision(entry: NotificationEntry, decision: DecisionImpl) { + // Not yet implemented. + } + + private fun logFullScreenIntentDecision(decision: FullScreenIntentDecisionImpl) { + // Not yet implemented. + } + + private fun checkSuppressors(entry: NotificationEntry) = + legacySuppressors.firstOrNull { it.suppressInterruptions(entry) } + + private fun checkAwakeSuppressors(entry: NotificationEntry) = + legacySuppressors.firstOrNull { it.suppressAwakeInterruptions(entry) } + + private fun checkAwakeHeadsUpSuppressors(entry: NotificationEntry) = + legacySuppressors.firstOrNull { it.suppressAwakeHeadsUp(entry) } + + private fun checkConditions(type: VisualInterruptionType): VisualInterruptionCondition? = + conditions.firstOrNull { it.types.contains(type) && it.shouldSuppress() } + + private fun checkFilters( + type: VisualInterruptionType, + entry: NotificationEntry + ): VisualInterruptionFilter? = + filters.firstOrNull { it.types.contains(type) && it.shouldSuppress(entry) } +} + +private const val TAG = "VisualInterruptionDecisionProviderImpl" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt new file mode 100644 index 000000000000..2624363c7dec --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.interruption + +import com.android.systemui.Flags +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the visual interruptions refactor flag state. */ +object VisualInterruptionRefactor { + const val FLAG_NAME = Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR + + /** Whether the refactor is enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.visualInterruptionsRefactor() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt index 4ef80e38bc63..39199df37bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.systemui.statusbar.notification.interruption import com.android.internal.logging.UiEventLogger.UiEventEnum @@ -50,6 +51,12 @@ sealed interface VisualInterruptionSuppressor { /** An optional UiEvent ID to be recorded when this suppresses an interruption. */ val uiEventId: UiEventEnum? + + /** + * Called after the suppressor is added to the [VisualInterruptionDecisionProvider] but before + * any other methods are called on the suppressor. + */ + fun start() {} } /** A reason why visual interruptions might be suppressed regardless of the notification. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index 2af7181f2f31..6785da4bf4f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -59,10 +59,8 @@ object SharedNotificationContainerBinder { launch { viewModel.position.collect { - controller.updateTopPadding( - it.top, - controller.isAddOrRemoveAnimationPending() - ) + val animate = it.animate || controller.isAddOrRemoveAnimationPending() + controller.updateTopPadding(it.top, animate) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 1229cb9b49ac..b86b5dcc7939 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -25,6 +25,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.util.kotlin.sample import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -118,8 +119,15 @@ constructor( } } } else { - interactor.topPosition.map { top -> - keyguardInteractor.sharedNotificationContainerPosition.value.copy(top = top) + interactor.topPosition.sample(shadeInteractor.qsExpansion, ::Pair).map { + (top, qsExpansion) -> + // When QS expansion > 0, it should directly set the top padding so do not + // animate it + val animate = qsExpansion == 0f + keyguardInteractor.sharedNotificationContainerPosition.value.copy( + top = top, + animate = animate + ) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index f899e2fb968a..5e7b85779599 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -505,7 +505,6 @@ open class AuthContainerViewTest : SysuiTestCase() { this.authenticators = authenticators } }, - featureFlags, testScope.backgroundScope, fingerprintProps, faceProps, @@ -519,9 +518,7 @@ open class AuthContainerViewTest : SysuiTestCase() { PromptViewModel( displayStateInteractor, promptSelectorInteractor, - vibrator, context, - featureFlags ), { credentialViewModel }, Handler(TestableLooper.get(this).looper), diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 885abcb72f1a..11c5d3bb27b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -88,7 +88,6 @@ import com.android.systemui.biometrics.domain.interactor.PromptCredentialInterac import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor; import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel; import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel; -import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.VibratorHelper; @@ -195,7 +194,6 @@ public class AuthControllerTest extends SysuiTestCase { private Handler mHandler; private DelayableExecutor mBackgroundExecutor; private TestableAuthController mAuthController; - private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Before public void setup() throws RemoteException { @@ -1023,7 +1021,7 @@ public class AuthControllerTest extends SysuiTestCase { private PromptInfo mLastBiometricPromptInfo; TestableAuthController(Context context) { - super(context, mFeatureFlags, null /* applicationCoroutineScope */, + super(context, null /* applicationCoroutineScope */, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, () -> mUdfpsController, () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index b695a0ee1fa3..d06cbbb5e433 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -44,18 +44,16 @@ import com.android.systemui.biometrics.shared.model.toSensorType import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.display.data.repository.FakeDisplayRepository -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -64,9 +62,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.mockito.Mock -import org.mockito.Mockito.never import org.mockito.Mockito.times -import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit private const val USER_ID = 4 @@ -95,7 +91,6 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private lateinit var selector: PromptSelectorInteractor private lateinit var viewModel: PromptViewModel private lateinit var iconViewModel: PromptIconViewModel - private val featureFlags = FakeFeatureFlags() @Before fun setup() { @@ -125,10 +120,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) selector.resetPrompt() - viewModel = - PromptViewModel(displayStateInteractor, selector, vibrator, mContext, featureFlags) + viewModel = PromptViewModel(displayStateInteractor, selector, mContext) iconViewModel = viewModel.iconViewModel - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false) } @Test @@ -180,26 +173,29 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = + fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - verify(vibrator, if (expectConfirmation) never() else times(1)) - .vibrateAuthSuccess(any()) + val confirmConstant by collectLastValue(viewModel.hapticsToPlay) + assertThat(confirmConstant) + .isEqualTo( + if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS + else HapticFeedbackConstants.CONFIRM + ) if (expectConfirmation) { viewModel.confirmAuthenticated() } - verify(vibrator).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) + val confirmedConstant by collectLastValue(viewModel.hapticsToPlay) + assertThat(confirmedConstant).isEqualTo(HapticFeedbackConstants.CONFIRM) } @Test - fun playSuccessHaptic_onwayHapticsEnabled_SetsConfirmConstant() = runGenericTest { - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true) + fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest { val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) @@ -212,8 +208,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun playErrorHaptic_onwayHapticsEnabled_SetsRejectConstant() = runGenericTest { - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true) + fun playErrorHaptic_SetsRejectConstant() = runGenericTest { viewModel.showTemporaryError("test", "messageAfterError", false) val currentConstant by collectLastValue(viewModel.hapticsToPlay) @@ -251,7 +246,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_fingerprint_to_error_landscape DisplayRotation.ROTATION_270 -> - R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright + R.raw + .biometricprompt_symbol_fingerprint_to_error_portrait_bottomright else -> throw Exception("invalid rotation") } assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset) @@ -496,7 +492,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_symbol_fingerprint_to_success_landscape DisplayRotation.ROTATION_270 -> - R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright + R.raw + .biometricprompt_symbol_fingerprint_to_success_portrait_bottomright else -> throw Exception("invalid rotation") } assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset) @@ -733,7 +730,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun plays_haptic_on_errors() = runGenericTest { + fun set_haptic_on_errors() = runGenericTest { viewModel.showTemporaryError( "so sad", messageAfterError = "", @@ -741,8 +738,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa hapticFeedback = true, ) - verify(vibrator).vibrateAuthError(any()) - verify(vibrator, never()).vibrateAuthSuccess(any()) + val constant by collectLastValue(viewModel.hapticsToPlay) + assertThat(constant).isEqualTo(HapticFeedbackConstants.REJECT) } @Test @@ -754,8 +751,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa hapticFeedback = false, ) - verify(vibrator, never()).vibrateAuthError(any()) - verify(vibrator, never()).vibrateAuthSuccess(any()) + val constant by collectLastValue(viewModel.hapticsToPlay) + assertThat(constant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS) } private suspend fun TestScope.showTemporaryErrors( diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index 80fe9e762832..fcb18f52086d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; import android.view.MotionEvent; import androidx.test.filters.SmallTest; @@ -35,13 +36,14 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionStateManager; +import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; import com.android.systemui.util.time.FakeSystemClock; @@ -54,8 +56,11 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import kotlinx.coroutines.flow.StateFlowKt; + @SmallTest @RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class FalsingCollectorImplTest extends SysuiTestCase { private FalsingCollectorImpl mFalsingCollector; @@ -73,7 +78,9 @@ public class FalsingCollectorImplTest extends SysuiTestCase { @Mock private KeyguardStateController mKeyguardStateController; @Mock - private ShadeExpansionStateManager mShadeExpansionStateManager; + private ShadeInteractor mShadeInteractor; + @Mock + private JavaAdapter mJavaAdapter; @Mock private BatteryController mBatteryController; @Mock @@ -88,12 +95,16 @@ public class FalsingCollectorImplTest extends SysuiTestCase { when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false)); mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager, mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor, - mStatusBarStateController, mKeyguardStateController, mShadeExpansionStateManager, - mBatteryController, - mDockManager, mFakeExecutor, mFakeSystemClock, () -> mSelectedUserInteractor); + mStatusBarStateController, mKeyguardStateController, + () -> mShadeInteractor, mBatteryController, + mDockManager, mFakeExecutor, + mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor + ); + mFalsingCollector.init(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt index 8416c46a3f38..6a79ee8553c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt @@ -24,8 +24,6 @@ import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.settings.ControlsSettingsDialogManager import com.android.systemui.controls.settings.FakeControlsSettingsRepository -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.statusbar.policy.KeyguardStateController @@ -83,8 +81,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { private lateinit var action: ControlActionCoordinatorImpl.Action private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository - private val featureFlags = FakeFeatureFlags() - @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -104,7 +100,6 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { metricsLogger, vibratorHelper, controlsSettingsRepository, - featureFlags )) coordinator.activityContext = mContext @@ -200,31 +195,7 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { } @Test - fun drag_isEdge_oneWayHapticsDisabled_usesVibrate() { - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false) - - coordinator.drag(cvh, true) - - verify(vibratorHelper).vibrate( - Vibrations.rangeEdgeEffect - ) - } - - @Test - fun drag_isNotEdge_oneWayHapticsDisabled_usesVibrate() { - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false) - - coordinator.drag(cvh, false) - - verify(vibratorHelper).vibrate( - Vibrations.rangeMiddleEffect - ) - } - - @Test - fun drag_isEdge_oneWayHapticsEnabled_usesPerformHapticFeedback() { - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true) - + fun drag_isEdge_performsSegmentTickHaptics() { coordinator.drag(cvh, true) verify(vibratorHelper).performHapticFeedback( @@ -234,9 +205,7 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { } @Test - fun drag_isNotEdge_oneWayHapticsEnabled_usesPerformHapticFeedback() { - featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true) - + fun drag_isNotEdge_performsFrequentTickHaptics() { coordinator.drag(cvh, false) verify(vibratorHelper).performHapticFeedback( diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 3af444a789c5..27fd3b13d55a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -165,12 +165,28 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { int maxBrightness = 3; when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(), eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness); + when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(), + eq(UserHandle.USER_CURRENT))) + .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); mScreen.transitionTo(UNINITIALIZED, INITIALIZED); assertEquals(maxBrightness, mServiceFake.screenBrightness); } @Test + public void testAod_usesLightSensorNotClampingToAutoBrightnessValue() { + int maxBrightness = 3; + when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(), + eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness); + when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(), + eq(UserHandle.USER_CURRENT))) + .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness); + } + + @Test public void doze_doesNotUseLightSensor() { // GIVEN the device is DOZE and the display state changes to ON mScreen.transitionTo(UNINITIALIZED, INITIALIZED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 2b280c05ba1b..814a317a72f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -253,7 +253,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mKeyguardStateController, mScreenOffAnimationController, mAuthController, - mShadeExpansionStateManager, () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt index 4f545cb0e288..b80771ff646c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt @@ -179,6 +179,8 @@ class KeyguardRootViewModelTest : SysuiTestCase() { val translationY by collectLastValue(underTest.translationY) val scale by collectLastValue(underTest.scale) + underTest.statusViewTop = 100 + // Set to dozing (on AOD) dozeAmountTransitionStep.emit(TransitionStep(value = 1f)) // Trigger a change to the burn-in model @@ -200,6 +202,37 @@ class KeyguardRootViewModelTest : SysuiTestCase() { } @Test + fun translationAndScaleFromBurnFullyDozingStaysOutOfTopInset() = + testScope.runTest { + val translationX by collectLastValue(underTest.translationX) + val translationY by collectLastValue(underTest.translationY) + val scale by collectLastValue(underTest.scale) + + underTest.statusViewTop = 100 + underTest.topInset = 80 + + // Set to dozing (on AOD) + dozeAmountTransitionStep.emit(TransitionStep(value = 1f)) + // Trigger a change to the burn-in model + burnInFlow.value = + BurnInModel( + translationX = 20, + translationY = -30, + scale = 0.5f, + ) + assertThat(translationX).isEqualTo(20) + // -20 instead of -30, due to inset of 80 + assertThat(translationY).isEqualTo(-20) + assertThat(scale).isEqualTo(Pair(0.5f, true /* scaleClockOnly */)) + + // Set to the beginning of GONE->AOD transition + goneToAodTransitionStep.emit(TransitionStep(value = 0f)) + assertThat(translationX).isEqualTo(0) + assertThat(translationY).isEqualTo(0) + assertThat(scale).isEqualTo(Pair(1f, true /* scaleClockOnly */)) + } + + @Test fun translationAndScaleFromBurnInUseScaleOnly() = testScope.runTest { whenever(clockController.config.useAlternateSmartspaceAODTransition).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt new file mode 100644 index 000000000000..89ba69fce9ad --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.custom + +import android.content.ComponentName +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.ServiceInfo +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults +import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository +import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +class CustomTileDefaultsRepositoryTest : SysuiTestCase() { + + @Mock private lateinit var sysuiContext: Context + @Mock private lateinit var user1Context: Context + @Mock private lateinit var user2Context: Context + @Mock private lateinit var packageManager1: PackageManager + @Mock private lateinit var packageManager2: PackageManager + + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + private lateinit var underTest: CustomTileDefaultsRepository + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + whenever(sysuiContext.createContextAsUser(eq(USER_1), any())).thenReturn(user1Context) + whenever(user1Context.packageManager).thenReturn(packageManager1) + packageManager1.setupApp1() + + whenever(sysuiContext.createContextAsUser(eq(USER_2), any())).thenReturn(user2Context) + whenever(user2Context.packageManager).thenReturn(packageManager2) + packageManager2.setupApp2() + + underTest = + CustomTileDefaultsRepositoryImpl( + sysuiContext, + testScope.backgroundScope, + testDispatcher, + ) + } + + @Test + fun regularRequestingEmitsTheNewDefault() = + testScope.runTest { + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + + runCurrent() + + val default = underTest.defaults(USER_1).first() as CustomTileDefaults.Result + assertThat(default.label).isEqualTo(APP_LABEL_1) + assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_1) + assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_1.packageName) + } + + @Test + fun requestingSystemAppEmitsTheNewDefault() = + testScope.runTest { + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + + runCurrent() + + val default = underTest.defaults(USER_1).first() as CustomTileDefaults.Result + assertThat(default.label).isEqualTo(APP_LABEL_1) + assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_1) + assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_1.packageName) + } + + @Test + fun requestingForcesTheNewEmit() = + testScope.runTest { + val defaults = mutableListOf<CustomTileDefaults.Result>() + backgroundScope.launch { + underTest + .defaults(USER_1) + .map { it as CustomTileDefaults.Result } + .collect { defaults.add(it) } + } + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + // the same request should be skipped. This leads to 2 result in assertions + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + runCurrent() + + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, true) + runCurrent() + + assertThat(defaults).hasSize(2) + assertThat(defaults.last().label).isEqualTo(APP_LABEL_1) + assertThat(defaults.last().icon.resId).isEqualTo(SERVICE_ICON_1) + assertThat(defaults.last().icon.resPackage).isEqualTo(COMPONENT_NAME_1.packageName) + } + + @Test + fun userChangeForcesTheNewEmit() = + testScope.runTest { + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + runCurrent() + + underTest.requestNewDefaults(USER_2, COMPONENT_NAME_2, false) + runCurrent() + + val default = underTest.defaults(USER_2).first() as CustomTileDefaults.Result + assertThat(default.label).isEqualTo(APP_LABEL_2) + assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_2) + assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_2.packageName) + } + + @Test + fun componentNameChangeForcesTheNewEmit() = + testScope.runTest { + packageManager1.setupApp2(false) + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + runCurrent() + + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_2, false) + runCurrent() + + val default = underTest.defaults(USER_1).first() as CustomTileDefaults.Result + assertThat(default.label).isEqualTo(APP_LABEL_2) + assertThat(default.icon.resId).isEqualTo(SERVICE_ICON_2) + assertThat(default.icon.resPackage).isEqualTo(COMPONENT_NAME_2.packageName) + } + + @Test + fun noIconIsAnError() = + testScope.runTest { + packageManager1.setupApp( + componentName = COMPONENT_NAME_1, + appLabel = "", + serviceIcon = 0, + appInfoIcon = 0, + isSystemApp = false, + ) + underTest.requestNewDefaults(USER_1, COMPONENT_NAME_1, false) + + runCurrent() + + assertThat(underTest.defaults(USER_1).first()) + .isInstanceOf(CustomTileDefaults.Error::class.java) + } + + @Test + fun applicationScopeIsFreedWhileNotSubscribed() = + testScope.runTest { + val listenJob = underTest.defaults(USER_1).launchIn(backgroundScope) + listenJob.cancel() + assertThat(this.coroutineContext[Job]!!.children.toList()).isEmpty() + } + + private fun PackageManager.setupApp1(isSystemApp: Boolean = false) = + setupApp( + componentName = COMPONENT_NAME_1, + serviceIcon = SERVICE_ICON_1, + appLabel = APP_LABEL_1, + appInfoIcon = APP_INFO_ICON_1, + isSystemApp = isSystemApp, + ) + private fun PackageManager.setupApp2(isSystemApp: Boolean = false) = + setupApp( + componentName = COMPONENT_NAME_2, + serviceIcon = SERVICE_ICON_2, + appLabel = APP_LABEL_2, + appInfoIcon = APP_INFO_ICON_2, + isSystemApp = isSystemApp, + ) + + private fun PackageManager.setupApp( + componentName: ComponentName, + serviceIcon: Int, + appLabel: CharSequence, + appInfoIcon: Int = serviceIcon, + isSystemApp: Boolean = false, + ) { + val appInfo = + object : ApplicationInfo() { + override fun isSystemApp(): Boolean = isSystemApp + } + .apply { icon = appInfoIcon } + whenever(getApplicationInfo(eq(componentName.packageName), any<Int>())).thenReturn(appInfo) + + // set of desired flags is different for system and a regular app. + var serviceFlags = + (PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE) + if (isSystemApp) { + serviceFlags = serviceFlags or PackageManager.MATCH_DISABLED_COMPONENTS + } + + val serviceInfo = + object : ServiceInfo() { + override fun loadLabel(pm: PackageManager): CharSequence = appLabel + } + .apply { + applicationInfo = appInfo + icon = serviceIcon + } + whenever(getServiceInfo(eq(componentName), eq(serviceFlags))).thenReturn(serviceInfo) + } + + private companion object { + val USER_1 = UserHandle(1) + val USER_2 = UserHandle(2) + + val COMPONENT_NAME_1 = ComponentName("pkg.test_1", "cls") + const val SERVICE_ICON_1 = 11 + const val APP_INFO_ICON_1 = 12 + const val APP_LABEL_1 = "app_1" + + val COMPONENT_NAME_2 = ComponentName("pkg.test_2", "cls") + const val SERVICE_ICON_2 = 21 + const val APP_INFO_ICON_2 = 22 + const val APP_LABEL_2 = "app_2" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 2ce4b04b037a..446a0b8116a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -99,7 +99,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.ui.view.KeyguardRootView; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; @@ -148,7 +147,6 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; @@ -335,7 +333,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; @Mock private JavaAdapter mJavaAdapter; @Mock private CastController mCastController; - @Mock private KeyguardRootView mKeyguardRootView; @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor; @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm; @@ -575,14 +572,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { PulseExpansionHandler expansionHandler = new PulseExpansionHandler( mContext, coordinator, - mKeyguardBypassController, mHeadsUpManager, - mock(NotificationRoundnessManager.class), + mKeyguardBypassController, + mHeadsUpManager, mConfigurationController, mStatusBarStateController, mFalsingManager, - mShadeExpansionStateManager, + mShadeInteractor, mLockscreenShadeTransitionController, - new FalsingCollectorFake(), mDumpManager); when(mKeyguardStatusViewComponentFactory.build(any(), any())) .thenReturn(mKeyguardStatusViewComponent); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 4ba850c570c4..8e0cf7d7f695 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -254,7 +254,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { mKeyguardStateController, mScreenOffAnimationController, mAuthController, - mShadeExpansionStateManager, () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt index e70dbc7f4fda..778cfa67cad7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade import android.testing.AndroidTestingRunner import android.testing.TestableLooper -import android.testing.TestableResources import android.view.View import android.view.ViewGroup import android.view.WindowInsets @@ -27,7 +26,6 @@ import androidx.annotation.IdRes import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags @@ -38,6 +36,8 @@ import com.android.systemui.navigationbar.NavigationModeController.ModeChangedLi import com.android.systemui.plugins.qs.QS import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener +import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.concurrency.FakeExecutor @@ -46,6 +46,7 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.function.Consumer +import kotlinx.coroutines.flow.MutableStateFlow import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -66,7 +67,7 @@ import org.mockito.MockitoAnnotations /** Uses Flags.MIGRATE_NSSL set to false. If all goes well, this set of tests will be deleted. */ @RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { @@ -74,7 +75,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { @Mock lateinit var navigationModeController: NavigationModeController @Mock lateinit var overviewProxyService: OverviewProxyService @Mock lateinit var shadeHeaderController: ShadeHeaderController - @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager + @Mock lateinit var shadeInteractor: ShadeInteractor @Mock lateinit var fragmentService: FragmentService @Mock lateinit var fragmentHostManager: FragmentHostManager @Mock @@ -88,7 +89,6 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { lateinit var underTest: NotificationsQSContainerController - private lateinit var fakeResources: TestableResources private lateinit var featureFlags: FakeFeatureFlags private lateinit var navigationModeCallback: ModeChangedListener private lateinit var taskbarVisibilityCallback: OverviewProxyListener @@ -111,6 +111,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { whenever(view.resources).thenReturn(mContext.resources) whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager) + whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false)) underTest = NotificationsQSContainerController( @@ -118,7 +119,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { navigationModeController, overviewProxyService, shadeHeaderController, - shadeExpansionStateManager, + shadeInteractor, fragmentService, delayableExecutor, featureFlags, @@ -475,7 +476,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() { navigationModeController, overviewProxyService, shadeHeaderController, - shadeExpansionStateManager, + shadeInteractor, fragmentService, delayableExecutor, featureFlags, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt index ac8c924fe689..23420037e233 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.shade import android.testing.AndroidTestingRunner import android.testing.TestableLooper -import android.testing.TestableResources import android.view.View import android.view.ViewGroup import android.view.WindowInsets @@ -27,7 +26,6 @@ import androidx.annotation.IdRes import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags @@ -38,6 +36,8 @@ import com.android.systemui.navigationbar.NavigationModeController.ModeChangedLi import com.android.systemui.plugins.qs.QS import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener +import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.concurrency.FakeExecutor @@ -46,6 +46,7 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.function.Consumer +import kotlinx.coroutines.flow.MutableStateFlow import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -65,7 +66,7 @@ import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class NotificationsQSContainerControllerTest : SysuiTestCase() { @@ -73,7 +74,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { @Mock lateinit var navigationModeController: NavigationModeController @Mock lateinit var overviewProxyService: OverviewProxyService @Mock lateinit var shadeHeaderController: ShadeHeaderController - @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager + @Mock lateinit var shadeInteractor: ShadeInteractor @Mock lateinit var fragmentService: FragmentService @Mock lateinit var fragmentHostManager: FragmentHostManager @Mock @@ -87,7 +88,6 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { lateinit var underTest: NotificationsQSContainerController - private lateinit var fakeResources: TestableResources private lateinit var featureFlags: FakeFeatureFlags private lateinit var navigationModeCallback: ModeChangedListener private lateinit var taskbarVisibilityCallback: OverviewProxyListener @@ -111,13 +111,15 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager) + whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false)) + underTest = NotificationsQSContainerController( view, navigationModeController, overviewProxyService, shadeHeaderController, - shadeExpansionStateManager, + shadeInteractor, fragmentService, delayableExecutor, featureFlags, @@ -458,7 +460,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() { navigationModeController, overviewProxyService, shadeHeaderController, - shadeExpansionStateManager, + shadeInteractor, fragmentService, delayableExecutor, featureFlags, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt index 6eeafefd0ac1..e920687753fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt @@ -189,4 +189,13 @@ class ShadeRepositoryImplTest : SysuiTestCase() { underTest.setUdfpsTransitionToFullShadeProgress(1f) assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(1f) } + + @Test + fun updateLegacyIsQsExpanded() = + testScope.runTest { + assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(false) + + underTest.setLegacyIsQsExpanded(true) + assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(true) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt index 20e5c43cba19..49e5c456e645 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -21,18 +21,17 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.ShadeExpansionStateManager +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableView -import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.util.mockito.mock +import kotlinx.coroutines.flow.MutableStateFlow import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -53,19 +52,18 @@ class PulseExpansionHandlerTest : SysuiTestCase() { private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock() private val bypassController: KeyguardBypassController = mock() private val headsUpManager: HeadsUpManager = mock() - private val roundnessManager: NotificationRoundnessManager = mock() private val configurationController: ConfigurationController = mock() private val statusBarStateController: StatusBarStateController = mock() private val falsingManager: FalsingManager = mock() - private val shadeExpansionStateManager: ShadeExpansionStateManager = mock() + private val shadeInteractor: ShadeInteractor = mock() private val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock() - private val falsingCollector: FalsingCollector = mock() private val dumpManager: DumpManager = mock() private val expandableView: ExpandableView = mock() @Before fun setUp() { whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight) + whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false)) pulseExpansionHandler = PulseExpansionHandler( @@ -73,13 +71,11 @@ class PulseExpansionHandlerTest : SysuiTestCase() { wakeUpCoordinator, bypassController, headsUpManager, - roundnessManager, configurationController, statusBarStateController, falsingManager, - shadeExpansionStateManager, + shadeInteractor, lockscreenShadeTransitionController, - falsingCollector, dumpManager ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt index 2e223f6d8c1f..df257ab113b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -27,7 +27,6 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule -import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController @@ -88,8 +87,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() { SystemEventChipAnimationController( context = mContext, statusBarWindowController = sbWindowController, - contentInsetsProvider = insetsProvider, - featureFlags = FakeFeatureFlags(), + contentInsetsProvider = insetsProvider ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt index c289ff3bc19d..bbc63f2009b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt @@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay -import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State.CONNECTED import com.android.systemui.privacy.PrivacyItemController import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.util.mockito.any @@ -48,7 +48,6 @@ import org.mockito.MockitoAnnotations class SystemEventCoordinatorTest : SysuiTestCase() { private val fakeSystemClock = FakeSystemClock() - private val featureFlags = FakeFeatureFlags() private val testScope = TestScope(UnconfinedTestDispatcher()) private val connectedDisplayInteractor = FakeConnectedDisplayInteractor() @@ -66,7 +65,6 @@ class SystemEventCoordinatorTest : SysuiTestCase() { batteryController, privacyController, context, - featureFlags, TestScope(UnconfinedTestDispatcher()), connectedDisplayInteractor ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt index fee8b82a3038..5f01b5a56e4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt @@ -26,8 +26,6 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.statusbar.BatteryStatusChip import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider @@ -76,7 +74,6 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { private lateinit var systemClock: FakeSystemClock private lateinit var chipAnimationController: SystemEventChipAnimationController private lateinit var systemStatusAnimationScheduler: SystemStatusAnimationScheduler - private val fakeFeatureFlags = FakeFeatureFlags() @get:Rule val animatorTestRule = AnimatorTestRule() @@ -84,15 +81,12 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) - fakeFeatureFlags.set(Flags.PLUG_IN_STATUS_BAR_CHIP, true) - systemClock = FakeSystemClock() chipAnimationController = SystemEventChipAnimationController( mContext, statusBarWindowController, - statusBarContentInsetProvider, - fakeFeatureFlags + statusBarContentInsetProvider ) // StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 50ce265b67d1..1c621613c403 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.systemui.statusbar.notification.interruption; +package com.android.systemui.statusbar.notification.interruption; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; @@ -27,6 +27,8 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; +import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; +import static android.provider.Settings.Global.HEADS_UP_ON; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; @@ -61,9 +63,9 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.systemui.res.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -74,6 +76,8 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.FakeGlobalSettings; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -120,6 +124,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { UserTracker mUserTracker; @Mock DeviceProvisionedController mDeviceProvisionedController; + FakeSystemClock mSystemClock; + FakeGlobalSettings mGlobalSettings; private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; @@ -129,10 +135,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser()); mUiEventLoggerFake = new UiEventLoggerFake(); + mSystemClock = new FakeSystemClock(); + mGlobalSettings = new FakeGlobalSettings(); + mGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON); mNotifInterruptionStateProvider = new NotificationInterruptStateProviderImpl( - mContext.getContentResolver(), mPowerManager, mAmbientDisplayConfiguration, mBatteryController, @@ -145,7 +153,9 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { mKeyguardNotificationVisibilityProvider, mUiEventLoggerFake, mUserTracker, - mDeviceProvisionedController); + mDeviceProvisionedController, + mSystemClock, + mGlobalSettings); mNotifInterruptionStateProvider.mUseHeadsUp = true; } @@ -426,7 +436,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } private long makeWhenHoursAgo(long hoursAgo) { - return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo); + return mSystemClock.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt index 947bcfb5011b..1d2055ec2e30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.notification.interruption import android.testing.AndroidTestingRunner @@ -19,27 +35,27 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() { - override val provider: VisualInterruptionDecisionProvider - get() = - NotificationInterruptStateProviderWrapper( - NotificationInterruptStateProviderImpl( - context.contentResolver, - powerManager, - ambientDisplayConfiguration, - batteryController, - statusBarStateController, - keyguardStateController, - headsUpManager, - logger, - mainHandler, - flags, - keyguardNotificationVisibilityProvider, - uiEventLogger, - userTracker, - deviceProvisionedController - ) - .also { it.mUseHeadsUp = true } + override val provider by lazy { + NotificationInterruptStateProviderWrapper( + NotificationInterruptStateProviderImpl( + powerManager, + ambientDisplayConfiguration, + batteryController, + statusBarStateController, + keyguardStateController, + headsUpManager, + logger, + mainHandler, + flags, + keyguardNotificationVisibilityProvider, + uiEventLogger, + userTracker, + deviceProvisionedController, + systemClock, + globalSettings, ) + ) + } // Tests of internals of the wrapper: diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt new file mode 100644 index 000000000000..ff89bdb6dbde --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.interruption + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() { + override val provider by lazy { + VisualInterruptionDecisionProviderImpl( + ambientDisplayConfiguration, + batteryController, + globalSettings, + headsUpManager, + logger, + mainHandler, + powerManager, + statusBarStateController, + systemClock, + userTracker, + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt index 6f4bbd5e21fc..df1228965749 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt @@ -1,20 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.systemui.statusbar.notification.interruption import android.app.ActivityManager import android.app.Notification import android.app.Notification.BubbleMetadata +import android.app.Notification.FLAG_BUBBLE +import android.app.Notification.VISIBILITY_PRIVATE import android.app.NotificationChannel import android.app.NotificationManager.IMPORTANCE_DEFAULT import android.app.NotificationManager.IMPORTANCE_HIGH +import android.app.NotificationManager.IMPORTANCE_LOW +import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT +import android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE import android.app.PendingIntent import android.app.PendingIntent.FLAG_MUTABLE +import android.content.Context import android.content.Intent import android.content.pm.UserInfo import android.graphics.drawable.Icon import android.hardware.display.FakeAmbientDisplayConfiguration -import android.os.Handler +import android.os.Looper import android.os.PowerManager +import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED +import android.provider.Settings.Global.HEADS_UP_OFF +import android.provider.Settings.Global.HEADS_UP_ON import com.android.internal.logging.testing.UiEventLoggerFake import com.android.systemui.SysuiTestCase import com.android.systemui.res.R @@ -23,16 +48,22 @@ import com.android.systemui.statusbar.FakeStatusBarStateController import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.MAX_HUN_WHEN_AGE_MS import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock +import com.android.systemui.util.settings.FakeGlobalSettings +import com.android.systemui.util.time.FakeSystemClock import com.android.systemui.utils.leaks.FakeBatteryController import com.android.systemui.utils.leaks.LeakCheckedTest +import com.android.systemui.utils.os.FakeHandler +import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -45,172 +76,503 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected val batteryController = FakeBatteryController(leakCheck) protected val deviceProvisionedController: DeviceProvisionedController = mock() protected val flags: NotifPipelineFlags = mock() + protected val globalSettings = FakeGlobalSettings() protected val headsUpManager: HeadsUpManager = mock() protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider = mock() protected val keyguardStateController: KeyguardStateController = mock() protected val logger: NotificationInterruptLogger = mock() - protected val mainHandler: Handler = mock() + protected val mainHandler = FakeHandler(Looper.getMainLooper()) protected val powerManager: PowerManager = mock() protected val statusBarStateController = FakeStatusBarStateController() + protected val systemClock = FakeSystemClock() protected val uiEventLogger = UiEventLoggerFake() protected val userTracker = FakeUserTracker() protected abstract val provider: VisualInterruptionDecisionProvider + private val neverSuppresses = object : NotificationInterruptSuppressor {} + + private val alwaysSuppressesInterruptions = + object : NotificationInterruptSuppressor { + override fun suppressInterruptions(entry: NotificationEntry?) = true + } + + private val alwaysSuppressesAwakeInterruptions = + object : NotificationInterruptSuppressor { + override fun suppressAwakeInterruptions(entry: NotificationEntry?) = true + } + + private val alwaysSuppressesAwakeHeadsUp = + object : NotificationInterruptSuppressor { + override fun suppressAwakeHeadsUp(entry: NotificationEntry?) = true + } + @Before fun setUp() { + globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON) + val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0) userTracker.set(listOf(user), /* currentUserIndex = */ 0) - whenever(headsUpManager.isSnoozed(any())).thenReturn(false) whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any())) .thenReturn(false) + + provider.start() } @Test fun testShouldPeek() { - ensureStateForPeek() + ensurePeekState() + assertShouldHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_settingDisabled() { + ensurePeekState { hunSettingEnabled = false } + assertShouldNotHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_packageSnoozed() { + ensurePeekState { hunSnoozed = true } + assertShouldNotHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldPeek_packageSnoozedButFsi() { + ensurePeekState { hunSnoozed = true } + assertShouldHeadsUp(buildFsiEntry()) + } + + @Test + fun testShouldNotPeek_alreadyBubbled() { + ensurePeekState { statusBarState = SHADE } + assertShouldNotHeadsUp(buildPeekEntry { isBubble = true }) + } + + @Test + fun testShouldPeek_isBubble_shadeLocked() { + ensurePeekState { statusBarState = SHADE_LOCKED } + assertShouldHeadsUp(buildPeekEntry { isBubble = true }) + } + + @Test + fun testShouldPeek_isBubble_keyguard() { + ensurePeekState { statusBarState = KEYGUARD } + assertShouldHeadsUp(buildPeekEntry { isBubble = true }) + } + + @Test + fun testShouldNotPeek_dnd() { + ensurePeekState() + assertShouldNotHeadsUp(buildPeekEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK }) + } - assertTrue(provider.makeUnloggedHeadsUpDecision(createPeekEntry()).shouldInterrupt) + @Test + fun testShouldNotPeek_notImportant() { + ensurePeekState() + assertShouldNotHeadsUp(buildPeekEntry { importance = IMPORTANCE_DEFAULT }) + } + + @Test + fun testShouldNotPeek_screenOff() { + ensurePeekState { isScreenOn = false } + assertShouldNotHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_dreaming() { + ensurePeekState { isDreaming = true } + assertShouldNotHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_oldWhen() { + ensurePeekState() + assertShouldNotHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) }) + } + + @Test + fun testShouldPeek_notQuiteOldEnoughWhen() { + ensurePeekState() + assertShouldHeadsUp(buildPeekEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS - 1) }) + } + + @Test + fun testShouldPeek_zeroWhen() { + ensurePeekState() + assertShouldHeadsUp(buildPeekEntry { whenMs = 0L }) + } + + @Test + fun testShouldPeek_oldWhenButFsi() { + ensurePeekState() + assertShouldHeadsUp(buildFsiEntry { whenMs = whenAgo(MAX_HUN_WHEN_AGE_MS) }) + } + + @Test + fun testShouldPeek_defaultLegacySuppressor() { + ensurePeekState() + provider.addLegacySuppressor(neverSuppresses) + assertShouldHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_legacySuppressInterruptions() { + ensurePeekState() + provider.addLegacySuppressor(alwaysSuppressesInterruptions) + assertShouldNotHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_legacySuppressAwakeInterruptions() { + ensurePeekState() + provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions) + assertShouldNotHeadsUp(buildPeekEntry()) + } + + @Test + fun testShouldNotPeek_legacySuppressAwakeHeadsUp() { + ensurePeekState() + provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp) + assertShouldNotHeadsUp(buildPeekEntry()) } @Test fun testShouldPulse() { - ensureStateForPulse() + ensurePulseState() + assertShouldHeadsUp(buildPulseEntry()) + } - assertTrue(provider.makeUnloggedHeadsUpDecision(createPulseEntry()).shouldInterrupt) + @Test + fun testShouldPulse_defaultLegacySuppressor() { + ensurePulseState() + provider.addLegacySuppressor(neverSuppresses) + assertShouldHeadsUp(buildPulseEntry()) } @Test - fun testShouldFsi_awake() { - ensureStateForAwakeFsi() + fun testShouldNotPulse_legacySuppressInterruptions() { + ensurePulseState() + provider.addLegacySuppressor(alwaysSuppressesInterruptions) + assertShouldNotHeadsUp(buildPulseEntry()) + } - assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt) + @Test + fun testShouldPulse_legacySuppressAwakeInterruptions() { + ensurePulseState() + provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions) + assertShouldHeadsUp(buildPulseEntry()) } @Test - fun testShouldFsi_dreaming() { - ensureStateForDreamingFsi() + fun testShouldPulse_legacySuppressAwakeHeadsUp() { + ensurePulseState() + provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp) + assertShouldHeadsUp(buildPulseEntry()) + } - assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt) + @Test + fun testShouldNotPulse_disabled() { + ensurePulseState { pulseOnNotificationsEnabled = false } + assertShouldNotHeadsUp(buildPulseEntry()) } @Test - fun testShouldFsi_keyguard() { - ensureStateForKeyguardFsi() + fun testShouldNotPulse_batterySaver() { + ensurePulseState { isAodPowerSave = true } + assertShouldNotHeadsUp(buildPulseEntry()) + } + + @Test + fun testShouldNotPulse_effectSuppressed() { + ensurePulseState() + assertShouldNotHeadsUp( + buildPulseEntry { suppressedVisualEffects = SUPPRESSED_EFFECT_AMBIENT } + ) + } - assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt) + @Test + fun testShouldNotPulse_visibilityOverridePrivate() { + ensurePulseState() + assertShouldNotHeadsUp(buildPulseEntry { visibilityOverride = VISIBILITY_PRIVATE }) + } + + @Test + fun testShouldNotPulse_importanceLow() { + ensurePulseState() + assertShouldNotHeadsUp(buildPulseEntry { importance = IMPORTANCE_LOW }) } @Test fun testShouldBubble() { - assertTrue(provider.makeAndLogBubbleDecision(createBubbleEntry()).shouldInterrupt) + ensureBubbleState() + assertShouldBubble(buildBubbleEntry()) } - private fun ensureStateForPeek() { - whenever(powerManager.isScreenOn).thenReturn(true) - statusBarStateController.dozing = false - statusBarStateController.dreaming = false + @Test + fun testShouldBubble_defaultLegacySuppressor() { + ensureBubbleState() + provider.addLegacySuppressor(neverSuppresses) + assertShouldBubble(buildBubbleEntry()) } - private fun ensureStateForPulse() { - ambientDisplayConfiguration.fakePulseOnNotificationEnabled = true - batteryController.setIsAodPowerSave(false) - statusBarStateController.dozing = true + @Test + fun testShouldNotBubble_legacySuppressInterruptions() { + ensureBubbleState() + provider.addLegacySuppressor(alwaysSuppressesInterruptions) + assertShouldNotBubble(buildBubbleEntry()) } - private fun ensureStateForAwakeFsi() { - whenever(powerManager.isInteractive).thenReturn(false) - statusBarStateController.dreaming = false - statusBarStateController.state = SHADE + @Test + fun testShouldNotBubble_legacySuppressAwakeInterruptions() { + ensureBubbleState() + provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions) + assertShouldNotBubble(buildBubbleEntry()) } - private fun ensureStateForDreamingFsi() { - whenever(powerManager.isInteractive).thenReturn(true) - statusBarStateController.dreaming = true - statusBarStateController.state = SHADE + @Test + fun testShouldBubble_legacySuppressAwakeHeadsUp() { + ensureBubbleState() + provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp) + assertShouldBubble(buildBubbleEntry()) } - private fun ensureStateForKeyguardFsi() { - whenever(powerManager.isInteractive).thenReturn(true) - statusBarStateController.dreaming = false - statusBarStateController.state = KEYGUARD + @Test + fun testShouldFsi_notInteractive() { + ensureNotInteractiveFsiState() + assertShouldFsi(buildFsiEntry()) } - private fun createNotif( - hasFsi: Boolean = false, - bubbleMetadata: BubbleMetadata? = null - ): Notification { - return Notification.Builder(context, TEST_CHANNEL_ID) - .apply { - setContentTitle(TEST_CONTENT_TITLE) - setContentText(TEST_CONTENT_TEXT) + @Test + fun testShouldFsi_dreaming() { + ensureDreamingFsiState() + assertShouldFsi(buildFsiEntry()) + } - if (hasFsi) { - setFullScreenIntent(mock(), /* highPriority = */ true) - } + @Test + fun testShouldFsi_keyguard() { + ensureKeyguardFsiState() + assertShouldFsi(buildFsiEntry()) + } - if (bubbleMetadata != null) { - setBubbleMetadata(bubbleMetadata) - } + private data class State( + var hunSettingEnabled: Boolean? = null, + var hunSnoozed: Boolean? = null, + var isAodPowerSave: Boolean? = null, + var isDozing: Boolean? = null, + var isDreaming: Boolean? = null, + var isInteractive: Boolean? = null, + var isScreenOn: Boolean? = null, + var keyguardShouldHideNotification: Boolean? = null, + var pulseOnNotificationsEnabled: Boolean? = null, + var statusBarState: Int? = null, + ) + + private fun setState(state: State): Unit = + state.run { + hunSettingEnabled?.let { + val newSetting = if (it) HEADS_UP_ON else HEADS_UP_OFF + globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, newSetting) } - .setContentTitle(TEST_CONTENT_TITLE) - .setContentText(TEST_CONTENT_TEXT) - .build() - } - private fun createBubbleMetadata(): BubbleMetadata { - val pendingIntent = - PendingIntent.getActivity( - context, - /* requestCode = */ 0, - Intent().setPackage(context.packageName), - FLAG_MUTABLE - ) + hunSnoozed?.let { whenever(headsUpManager.isSnoozed(TEST_PACKAGE)).thenReturn(it) } - val icon = Icon.createWithResource(context.resources, R.drawable.android) + isAodPowerSave?.let { batteryController.setIsAodPowerSave(it) } - return BubbleMetadata.Builder(pendingIntent, icon).build() - } + isDozing?.let { statusBarStateController.dozing = it } + + isDreaming?.let { statusBarStateController.dreaming = it } + + isInteractive?.let { whenever(powerManager.isInteractive).thenReturn(it) } - private fun createEntry( - notif: Notification, - importance: Int = IMPORTANCE_DEFAULT, - canBubble: Boolean? = null - ): NotificationEntry { - return NotificationEntryBuilder() + isScreenOn?.let { whenever(powerManager.isScreenOn).thenReturn(it) } + + keyguardShouldHideNotification?.let { + whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any())) + .thenReturn(it) + } + + pulseOnNotificationsEnabled?.let { + ambientDisplayConfiguration.fakePulseOnNotificationEnabled = it + } + + statusBarState?.let { statusBarStateController.state = it } + } + + private fun ensureState(block: State.() -> Unit) = + State() .apply { - setPkg(TEST_PACKAGE) - setOpPkg(TEST_PACKAGE) - setTag(TEST_TAG) - setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)) - setNotification(notif) - setImportance(importance) - - if (canBubble != null) { - setCanBubble(canBubble) - } + keyguardShouldHideNotification = false + apply(block) } - .build() + .run(this::setState) + + private fun ensurePeekState(block: State.() -> Unit = {}) = ensureState { + hunSettingEnabled = true + hunSnoozed = false + isDozing = false + isDreaming = false + isScreenOn = true + run(block) } - private fun createPeekEntry() = createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH) + private fun ensurePulseState(block: State.() -> Unit = {}) = ensureState { + isAodPowerSave = false + isDozing = true + pulseOnNotificationsEnabled = true + run(block) + } + + private fun ensureBubbleState(block: State.() -> Unit = {}) = ensureState(block) + + private fun ensureNotInteractiveFsiState(block: State.() -> Unit = {}) = ensureState { + isDreaming = false + isInteractive = false + statusBarState = SHADE + run(block) + } + + private fun ensureDreamingFsiState(block: State.() -> Unit = {}) = ensureState { + isDreaming = true + isInteractive = true + statusBarState = SHADE + run(block) + } + + private fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState { + isDreaming = false + isInteractive = true + statusBarState = KEYGUARD + run(block) + } + + private fun assertShouldHeadsUp(entry: NotificationEntry) = + provider.makeUnloggedHeadsUpDecision(entry).let { + assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt) + } - private fun createPulseEntry() = - createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH).also { - modifyRanking(it).setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build() + private fun assertShouldNotHeadsUp(entry: NotificationEntry) = + provider.makeUnloggedHeadsUpDecision(entry).let { + assertFalse("unexpected unsuppressed HUN: ${it.logReason}", it.shouldInterrupt) } - private fun createFsiEntry() = - createEntry(notif = createNotif(hasFsi = true), importance = IMPORTANCE_HIGH) + private fun assertShouldBubble(entry: NotificationEntry) = + provider.makeAndLogBubbleDecision(entry).let { + assertTrue("unexpected suppressed bubble: ${it.logReason}", it.shouldInterrupt) + } - private fun createBubbleEntry() = - createEntry( - notif = createNotif(bubbleMetadata = createBubbleMetadata()), - importance = IMPORTANCE_HIGH, - canBubble = true - ) + private fun assertShouldNotBubble(entry: NotificationEntry) = + provider.makeAndLogBubbleDecision(entry).let { + assertFalse("unexpected unsuppressed bubble: ${it.logReason}", it.shouldInterrupt) + } + + private fun assertShouldFsi(entry: NotificationEntry) = + provider.makeUnloggedFullScreenIntentDecision(entry).let { + assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt) + } + + private fun assertShouldNotFsi(entry: NotificationEntry) = + provider.makeUnloggedFullScreenIntentDecision(entry).let { + assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt) + } + + private class EntryBuilder(val context: Context) { + var importance = IMPORTANCE_DEFAULT + var suppressedVisualEffects: Int? = null + var whenMs: Long? = null + var visibilityOverride: Int? = null + var hasFsi = false + var canBubble: Boolean? = null + var isBubble = false + var hasBubbleMetadata = false + var bubbleSuppressNotification: Boolean? = null + + private fun buildBubbleMetadata() = + BubbleMetadata.Builder( + PendingIntent.getActivity( + context, + /* requestCode = */ 0, + Intent().setPackage(context.packageName), + FLAG_MUTABLE + ), + Icon.createWithResource(context.resources, R.drawable.android) + ) + .apply { bubbleSuppressNotification?.let { setSuppressNotification(it) } } + .build() + + fun build() = + Notification.Builder(context, TEST_CHANNEL_ID) + .apply { + setContentTitle(TEST_CONTENT_TITLE) + setContentText(TEST_CONTENT_TEXT) + + if (hasFsi) { + setFullScreenIntent(mock(), /* highPriority = */ true) + } + + whenMs?.let { setWhen(it) } + + if (hasBubbleMetadata) { + setBubbleMetadata(buildBubbleMetadata()) + } + } + .build() + .apply { + if (isBubble) { + flags = flags or FLAG_BUBBLE + } + } + .let { NotificationEntryBuilder().setNotification(it) } + .apply { + setPkg(TEST_PACKAGE) + setOpPkg(TEST_PACKAGE) + setTag(TEST_TAG) + + setImportance(importance) + setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance)) + + canBubble?.let { setCanBubble(it) } + } + .build()!! + .also { + modifyRanking(it) + .apply { + suppressedVisualEffects?.let { setSuppressedVisualEffects(it) } + visibilityOverride?.let { setVisibilityOverride(it) } + } + .build() + } + } + + private fun buildEntry(block: EntryBuilder.() -> Unit) = + EntryBuilder(context).also(block).build() + + private fun buildPeekEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { + importance = IMPORTANCE_HIGH + run(block) + } + + private fun buildPulseEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { + importance = IMPORTANCE_DEFAULT + visibilityOverride = VISIBILITY_NO_OVERRIDE + run(block) + } + + private fun buildBubbleEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { + canBubble = true + hasBubbleMetadata = true + run(block) + } + + private fun buildFsiEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry { + importance = IMPORTANCE_HIGH + hasFsi = true + run(block) + } + + private fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs } private const val TEST_CONTENT_TITLE = "Test Content Title" diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index 60421c981e6d..3a9d111bacf7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -314,7 +314,26 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { sharedNotificationContainerInteractor.setTopPosition(10f) assertThat(position) - .isEqualTo(SharedNotificationContainerPosition(top = 10f, bottom = 0f)) + .isEqualTo( + SharedNotificationContainerPosition(top = 10f, bottom = 0f, animate = true) + ) + } + + @Test + fun positionOnQS() = + testScope.runTest { + val position by collectLastValue(underTest.position) + + // Start on lockscreen with shade expanded + showLockscreenWithQSExpanded() + + // When not in split shade + sharedNotificationContainerInteractor.setTopPosition(10f) + + assertThat(position) + .isEqualTo( + SharedNotificationContainerPosition(top = 10f, bottom = 0f, animate = false) + ) } @Test @@ -390,6 +409,17 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) } + private suspend fun TestScope.showLockscreenWithQSExpanded() { + shadeRepository.setLockscreenShadeExpansion(0f) + shadeRepository.setQsExpansion(1f) + keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) + keyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.AOD, + to = KeyguardState.LOCKSCREEN, + this, + ) + } + @SysUISingleton @Component( modules = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 05fd6d22f961..4a20f831a781 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -20,6 +20,8 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; +import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; +import static android.provider.Settings.Global.HEADS_UP_ON; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -51,7 +53,6 @@ import android.app.NotificationChannel; import android.app.WallpaperManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.IntentFilter; import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientDisplayConfiguration; @@ -180,11 +181,16 @@ import com.android.systemui.util.WallpaperController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.MessageRouterImpl; import com.android.systemui.util.kotlin.JavaAdapter; +import com.android.systemui.util.settings.FakeGlobalSettings; +import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.time.SystemClock; import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -198,8 +204,6 @@ import java.util.Optional; import javax.inject.Provider; -import dagger.Lazy; - @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -319,6 +323,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { private ShadeController mShadeController; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); private final FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock); private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @@ -349,8 +354,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, Handler.createAsync(Looper.myLooper())); + mFakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON); + mNotificationInterruptStateProvider = - new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), + new TestableNotificationInterruptStateProviderImpl( mPowerManager, mAmbientDisplayConfiguration, mStatusBarStateController, @@ -363,7 +370,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mock(KeyguardNotificationVisibilityProvider.class), mock(UiEventLogger.class), mUserTracker, - mDeviceProvisionedController); + mDeviceProvisionedController, + mFakeSystemClock, + mFakeGlobalSettings); mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class)); mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class)); @@ -1162,7 +1171,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { NotificationInterruptStateProviderImpl { TestableNotificationInterruptStateProviderImpl( - ContentResolver contentResolver, PowerManager powerManager, AmbientDisplayConfiguration ambientDisplayConfiguration, StatusBarStateController controller, @@ -1175,9 +1183,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, UiEventLogger uiEventLogger, UserTracker userTracker, - DeviceProvisionedController deviceProvisionedController) { + DeviceProvisionedController deviceProvisionedController, + SystemClock systemClock, + GlobalSettings globalSettings) { super( - contentResolver, powerManager, ambientDisplayConfiguration, batteryController, @@ -1190,7 +1199,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { keyguardNotificationVisibilityProvider, uiEventLogger, userTracker, - deviceProvisionedController + deviceProvisionedController, + systemClock, + globalSettings ); mUseHeadsUp = true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index ec808c796d46..8309b85620bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -18,6 +18,8 @@ package com.android.systemui.wmshell; import static android.app.Notification.FLAG_BUBBLE; import static android.app.PendingIntent.FLAG_MUTABLE; +import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; +import static android.provider.Settings.Global.HEADS_UP_ON; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL; @@ -157,6 +159,8 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.user.domain.interactor.UserSwitcherInteractor; +import com.android.systemui.util.settings.FakeGlobalSettings; +import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.Bubble; @@ -476,7 +480,6 @@ public class BubblesTest extends SysuiTestCase { mKeyguardStateController, mScreenOffAnimationController, mAuthController, - mShadeExpansionStateManager, () -> mShadeInteractor, mShadeWindowLogger, () -> mSelectedUserInteractor @@ -507,8 +510,11 @@ public class BubblesTest extends SysuiTestCase { when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn( Collections.singletonList(mock(UserInfo.class))); + final FakeGlobalSettings fakeGlobalSettings = new FakeGlobalSettings(); + fakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON); + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = - new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), + new TestableNotificationInterruptStateProviderImpl( mock(PowerManager.class), mock(AmbientDisplayConfiguration.class), mock(StatusBarStateController.class), @@ -521,7 +527,9 @@ public class BubblesTest extends SysuiTestCase { mock(KeyguardNotificationVisibilityProvider.class), mock(UiEventLogger.class), mock(UserTracker.class), - mock(DeviceProvisionedController.class) + mock(DeviceProvisionedController.class), + mock(SystemClock.class), + fakeGlobalSettings ); mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java index 0df235dd2416..975555c1a46b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java @@ -16,7 +16,6 @@ package com.android.systemui.wmshell; -import android.content.ContentResolver; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.PowerManager; @@ -32,12 +31,13 @@ import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.time.SystemClock; public class TestableNotificationInterruptStateProviderImpl extends NotificationInterruptStateProviderImpl { TestableNotificationInterruptStateProviderImpl( - ContentResolver contentResolver, PowerManager powerManager, AmbientDisplayConfiguration ambientDisplayConfiguration, StatusBarStateController statusBarStateController, @@ -50,8 +50,10 @@ public class TestableNotificationInterruptStateProviderImpl KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, UiEventLogger uiEventLogger, UserTracker userTracker, - DeviceProvisionedController deviceProvisionedController) { - super(contentResolver, + DeviceProvisionedController deviceProvisionedController, + SystemClock systemClock, + GlobalSettings globalSettings) { + super( powerManager, ambientDisplayConfiguration, batteryController, @@ -64,7 +66,9 @@ public class TestableNotificationInterruptStateProviderImpl keyguardNotificationVisibilityProvider, uiEventLogger, userTracker, - deviceProvisionedController); + deviceProvisionedController, + systemClock, + globalSettings); mUseHeadsUp = true; } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt new file mode 100644 index 000000000000..13910fd5c564 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.impl.custom.data.repository + +import android.content.ComponentName +import android.os.UserHandle +import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository { + + private val defaults: MutableMap<DefaultsKey, CustomTileDefaults> = mutableMapOf() + private val defaultsFlow = MutableSharedFlow<DefaultsRequest>() + + private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf() + val defaultsRequests: List<DefaultsRequest> = mutableDefaultsRequests + + override fun defaults(user: UserHandle): Flow<CustomTileDefaults> = + defaultsFlow + .distinctUntilChanged { old, new -> + if (new.force) { + false + } else { + old == new + } + } + .map { defaults[DefaultsKey(it.user, it.componentName)]!! } + + override fun requestNewDefaults( + user: UserHandle, + componentName: ComponentName, + force: Boolean + ) { + val request = DefaultsRequest(user, componentName, force) + mutableDefaultsRequests.add(request) + defaultsFlow.tryEmit(request) + } + + fun putDefaults( + user: UserHandle, + componentName: ComponentName, + customTileDefaults: CustomTileDefaults, + ) { + defaults[DefaultsKey(user, componentName)] = customTileDefaults + } + + fun removeDefaults(user: UserHandle, componentName: ComponentName) { + defaults.remove(DefaultsKey(user, componentName)) + } + + data class DefaultsRequest( + val user: UserHandle, + val componentName: ComponentName, + val force: Boolean = false, + ) + + private data class DefaultsKey(val user: UserHandle, val componentName: ComponentName) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt index 3c49c58580cc..800593fe61a1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt @@ -56,6 +56,14 @@ class FakeShadeRepository @Inject constructor() : ShadeRepository { @Deprecated("Use ShadeInteractor instead") override val legacyExpandedOrAwaitingInputTransfer = _legacyExpandedOrAwaitingInputTransfer + private val _legacyIsQsExpanded = MutableStateFlow(false) + @Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded + + @Deprecated("Use ShadeInteractor instead") + override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) { + _legacyIsQsExpanded.value = legacyIsQsExpanded + } + @Deprecated("Use ShadeInteractor instead") override fun setLegacyExpandedOrAwaitingInputTransfer( legacyExpandedOrAwaitingInputTransfer: Boolean diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt index 19fdb6ddad02..a65813abd49f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt @@ -104,7 +104,7 @@ class FakeStatusBarStateController : SysuiStatusBarStateController { override fun isDreaming() = dreaming - override fun setIsDreaming(drreaming: Boolean): Boolean { + override fun setIsDreaming(dreaming: Boolean): Boolean { dreaming != this.dreaming || return false this.dreaming = dreaming callbacks.forEach { it.onDreamingChanged(dreaming) } diff --git a/packages/SystemUI/tools/lint/baseline.xml b/packages/SystemUI/tools/lint/baseline.xml index 301c9b831c3b..43f830006504 100644 --- a/packages/SystemUI/tools/lint/baseline.xml +++ b/packages/SystemUI/tools/lint/baseline.xml @@ -1271,17 +1271,6 @@ </issue> <issue - id="MergeRootFrame" - message="This `<FrameLayout>` can be replaced with a `<merge>` tag" - errorLine1="<FrameLayout" - errorLine2="^"> - <location - file="res/layout/volume_dnd_icon.xml" - line="16" - column="1"/> - </issue> - - <issue id="InefficientWeight" message="Use a `layout_height` of `0dp` instead of `wrap_content` for better performance" errorLine1=" android:layout_height="wrap_content"" @@ -88853,17 +88842,6 @@ errorLine1=" <ImageView" errorLine2=" ^"> <location - file="res/layout/volume_dnd_icon.xml" - line="23" - column="5"/> - </issue> - - <issue - id="ContentDescription" - message="[Accessibility] Missing `contentDescription` attribute on image" - errorLine1=" <ImageView" - errorLine2=" ^"> - <location file="res/layout/wireless_charging_layout.xml" line="26" column="5"/> @@ -89783,15 +89761,4 @@ column="22"/> </issue> - <issue - id="RtlHardcoded" - message="Use "`end`" instead of "`right`" to ensure correct behavior in right-to-left locales" - errorLine1=" android:layout_gravity="right|top"" - errorLine2=" ~~~~~~~~~"> - <location - file="res/layout/volume_dnd_icon.xml" - line="26" - column="33"/> - </issue> - </issues> diff --git a/services/Android.bp b/services/Android.bp index aca8409c284f..f1534b461607 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -41,7 +41,10 @@ soong_config_module_type { name: "system_optimized_java_defaults", module_type: "java_defaults", config_namespace: "ANDROID", - bool_variables: ["SYSTEM_OPTIMIZE_JAVA"], + bool_variables: [ + "SYSTEM_OPTIMIZE_JAVA", + "FULL_SYSTEM_OPTIMIZE_JAVA", + ], properties: [ "optimize", "dxflags", @@ -56,6 +59,7 @@ system_optimized_java_defaults { enabled: true, // TODO(b/210510433): Enable optimizations after improving // retracing infra. + // See also FULL_SYSTEM_OPTIMIZE_JAVA. optimize: false, shrink: true, ignore_warnings: false, @@ -81,6 +85,12 @@ system_optimized_java_defaults { dxflags: ["--debug"], }, }, + // Allow form factors to opt-in full system java optimization + FULL_SYSTEM_OPTIMIZE_JAVA: { + optimize: { + optimize: true, + }, + }, }, } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 32666e76372b..bb50a991d9c1 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3406,7 +3406,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // displays in one display. It's not a real display and there's no input events for it. final ArrayList<Display> displays = getValidDisplayList(); if (userState.isMagnificationSingleFingerTripleTapEnabledLocked() - || userState.isMagnificationTwoFingerTripleTapEnabledLocked() + || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() + && userState.isMagnificationTwoFingerTripleTapEnabledLocked()) || userState.isShortcutMagnificationEnabledLocked()) { for (int i = 0; i < displays.size(); i++) { final Display display = displays.get(i); @@ -3435,7 +3436,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } final boolean connect = (userState.isShortcutMagnificationEnabledLocked() - || userState.isMagnificationSingleFingerTripleTapEnabledLocked()) + || userState.isMagnificationSingleFingerTripleTapEnabledLocked() + || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() + && userState.isMagnificationTwoFingerTripleTapEnabledLocked())) && (userState.getMagnificationCapabilitiesLocked() != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) || userHasMagnificationServicesLocked(userState); @@ -5133,6 +5136,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Remove magnification button UI when the magnification capability is not all mode or // magnification is disabled. if (!(userState.isMagnificationSingleFingerTripleTapEnabledLocked() + || (Flags.enableMagnificationMultipleFingerMultipleTapGesture() + && userState.isMagnificationTwoFingerTripleTapEnabledLocked()) || userState.isShortcutMagnificationEnabledLocked()) || userState.getMagnificationCapabilitiesLocked() != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 959f69ea483f..92af68bc40a3 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -361,14 +361,6 @@ public class VirtualDeviceManagerService extends SystemService { @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) { createVirtualDevice_enforcePermission(); attributionSource.enforceCallingUid(); - final long identity = Binder.clearCallingIdentity(); - try { - if (Flags.moreLogs()) { - Slog.i(TAG, "Creating VirtualDevice"); - } - } finally { - Binder.restoreCallingIdentity(identity); - } final int callingUid = getCallingUid(); final String packageName = attributionSource.getPackageName(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d61dd7543b3e..88bb66f8ef22 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -15947,7 +15947,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { sdkSandboxInfo = sandboxManagerLocal.getSdkSandboxApplicationInfoForInstrumentation( - sdkSandboxClientAppInfo, userId, isSdkInSandbox); + sdkSandboxClientAppInfo, isSdkInSandbox); } catch (NameNotFoundException e) { reportStartInstrumentationFailureLocked( watcher, className, "Can't find SdkSandbox package"); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 614caffe2f57..f7d040834065 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2414,6 +2414,18 @@ public final class ProcessList { allowlistedAppDataInfoMap = null; } + boolean bindOverrideSysprops = false; + String[] syspropOverridePkgNames = DeviceConfig.getString( + DeviceConfig.NAMESPACE_APP_COMPAT, + "appcompat_sysprop_override_pkgs", "").split(","); + String[] pkgs = app.getPackageList(); + for (int i = 0; i < pkgs.length; i++) { + if (ArrayUtils.contains(syspropOverridePkgNames, pkgs[i])) { + bindOverrideSysprops = true; + break; + } + } + AppStateTracker ast = mService.mServices.mAppStateTracker; if (ast != null) { final boolean inBgRestricted = ast.isAppBackgroundRestricted( @@ -2436,6 +2448,7 @@ public final class ProcessList { app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, app.getDisabledCompatChanges(), + bindOverrideSysprops, new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()}); } else if (hostingRecord.usesAppZygote()) { final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app); @@ -2447,7 +2460,7 @@ public final class ProcessList { app.info.dataDir, null, app.info.packageName, /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap, - false, false, + false, false, bindOverrideSysprops, new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()}); } else { regularZygote = true; @@ -2457,6 +2470,7 @@ public final class ProcessList { app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags, isTopApp, app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs, + bindOverrideSysprops, new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()}); // By now the process group should have been created by zygote. app.mProcessGroupCreated = true; @@ -3265,12 +3279,17 @@ public final class ProcessList { // Check if we should mark the processrecord for first launch after force-stopping if ((r.getApplicationInfo().flags & ApplicationInfo.FLAG_STOPPED) != 0) { - final boolean wasPackageEverLaunched = mService.getPackageManagerInternal() - .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId); - // If the package was launched in the past but is currently stopped, only then it - // should be considered as stopped after use. Do not mark it if it's the first launch. - if (wasPackageEverLaunched) { - r.setWasForceStopped(true); + try { + final boolean wasPackageEverLaunched = mService.getPackageManagerInternal() + .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId); + // If the package was launched in the past but is currently stopped, only then it + // should be considered as stopped after use. Do not mark it if it's the + // first launch. + if (wasPackageEverLaunched) { + r.setWasForceStopped(true); + } + } catch (IllegalArgumentException e) { + // App doesn't have state yet, so wasn't forcestopped } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 08503cb2e9f8..3e46ee29346e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -50,6 +50,8 @@ public abstract class InputMethodManagerInternal { /** * Called by the power manager to tell the input method manager whether it * should start watching for wake events. + * + * @param interactive the interactive mode parameter */ public abstract void setInteractive(boolean interactive); @@ -61,16 +63,16 @@ public abstract class InputMethodManagerInternal { /** * Returns the list of installed input methods for the specified user. * - * @param userId The user ID to be queried. - * @return A list of {@link InputMethodInfo}. VR-only IMEs are already excluded. + * @param userId the user ID to be queried + * @return a list of {@link InputMethodInfo}. VR-only IMEs are already excluded */ public abstract List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId); /** * Returns the list of installed input methods that are enabled for the specified user. * - * @param userId The user ID to be queried. - * @return A list of {@link InputMethodInfo} that are enabled for {@code userId}. + * @param userId the user ID to be queried + * @return a list of {@link InputMethodInfo} that are enabled for {@code userId} */ public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId); @@ -78,8 +80,10 @@ public abstract class InputMethodManagerInternal { * Called by the Autofill Frameworks to request an {@link InlineSuggestionsRequest} from * the input method. * + * @param userId the user ID to be queried * @param requestInfo information needed to create an {@link InlineSuggestionsRequest}. - * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object. + * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request + * object */ public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId, InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb); @@ -88,8 +92,8 @@ public abstract class InputMethodManagerInternal { * Force switch to the enabled input method by {@code imeId} for current user. If the input * method with {@code imeId} is not enabled or not installed, do nothing. * - * @param imeId The input method ID to be switched to. - * @param userId The user ID to be queried. + * @param imeId the input method ID to be switched to + * @param userId the user ID to be queried * @return {@code true} if the current input method was successfully switched to the input * method by {@code imeId}; {@code false} the input method with {@code imeId} is not available * to be switched. @@ -100,28 +104,30 @@ public abstract class InputMethodManagerInternal { * Force enable or disable the input method associated with {@code imeId} for given user. If * the input method associated with {@code imeId} is not installed, do nothing. * - * @param imeId The input method ID to be enabled or disabled. + * @param imeId the input method ID to be enabled or disabled * @param enabled {@code true} if the input method associated with {@code imeId} should be - * enabled. - * @param userId The user ID to be queried. + * enabled + * @param userId the user ID to be queried * @return {@code true} if the input method associated with {@code imeId} was successfully - * enabled or disabled, {@code false} if the input method specified is not installed - * or was unable to be enabled/disabled for some other reason. + * enabled or disabled, {@code false} if the input method specified is not installed + * or was unable to be enabled/disabled for some other reason. */ public abstract boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId); /** * Registers a new {@link InputMethodListListener}. + * + * @param listener the listener to add */ public abstract void registerInputMethodListListener(InputMethodListListener listener); /** * Transfers input focus from a given input token to that of the IME window. * - * @param sourceInputToken The source token. - * @param displayId The display hosting the IME window. - * @return {@code true} if the transfer is successful. + * @param sourceInputToken the source token. + * @param displayId the display hosting the IME window + * @return {@code true} if the transfer is successful */ public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken, int displayId); @@ -132,7 +138,7 @@ public abstract class InputMethodManagerInternal { * or SystemUI). * * @param windowToken the window token that is now in control, or {@code null} if no client - * window is in control of the IME. + * window is in control of the IME */ public abstract void reportImeControl(@Nullable IBinder windowToken); @@ -163,8 +169,8 @@ public abstract class InputMethodManagerInternal { * Callback when the IInputMethodSession from the accessibility service with the specified * accessibilityConnectionId is created. * - * @param accessibilityConnectionId The connection id of the accessibility service. - * @param session The session passed back from the accessibility service. + * @param accessibilityConnectionId the connection id of the accessibility service + * @param session the session passed back from the accessibility service */ public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId, IAccessibilityInputMethodSession session); @@ -173,7 +179,7 @@ public abstract class InputMethodManagerInternal { * Unbind the accessibility service with the specified accessibilityConnectionId from current * client. * - * @param accessibilityConnectionId The connection id of the accessibility service. + * @param accessibilityConnectionId the connection id of the accessibility service */ public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId); @@ -181,7 +187,7 @@ public abstract class InputMethodManagerInternal { * Switch the keyboard layout in response to a keyboard shortcut. * * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the - * previous subtype. + * previous subtype */ public abstract void switchKeyboardLayout(int direction); @@ -192,7 +198,7 @@ public abstract class InputMethodManagerInternal { public abstract boolean isAnyInputConnectionActive(); /** - * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. + * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = new InputMethodManagerInternal() { @@ -282,7 +288,7 @@ public abstract class InputMethodManagerInternal { }; /** - * @return Global instance if exists. Otherwise, a fallback no-op instance. + * @return Global instance if exists. Otherwise, a fallback no-op instance. */ @NonNull public static InputMethodManagerInternal get() { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 9dec1dff4cf0..d456a7478551 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -192,6 +192,7 @@ class MediaRouter2ServiceImpl { // Start of methods that implement MediaRouter2 operations. + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) @NonNull public boolean verifyPackageExists(@NonNull String clientPackageName) { final int pid = Binder.getCallingPid(); @@ -199,11 +200,7 @@ class MediaRouter2ServiceImpl { final long token = Binder.clearCallingIdentity(); try { - mContext.enforcePermission( - Manifest.permission.MEDIA_CONTENT_CONTROL, - pid, - uid, - "Must hold MEDIA_CONTENT_CONTROL permission."); + enforcePrivilegedRoutingPermissions(uid, pid); PackageManager pm = mContext.getPackageManager(); pm.getPackageInfo(clientPackageName, PackageManager.PackageInfoFlags.of(0)); return true; @@ -482,6 +479,7 @@ class MediaRouter2ServiceImpl { } } + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerManager(@NonNull IMediaRouter2Manager manager, @NonNull String callerPackageName) { Objects.requireNonNull(manager, "manager must not be null"); @@ -729,6 +727,15 @@ class MediaRouter2ServiceImpl { return hasBluetoothRoutingPermission; } + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) + private void enforcePrivilegedRoutingPermissions(int callerUid, int callerPid) { + mContext.enforcePermission( + Manifest.permission.MEDIA_CONTENT_CONTROL, + callerPid, + callerUid, + "Must hold MEDIA_CONTENT_CONTROL permission."); + } + // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager. public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { @@ -837,15 +844,18 @@ class MediaRouter2ServiceImpl { private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) { RouterRecord routerRecord = mAllRouterRecords.remove(router.asBinder()); if (routerRecord == null) { - Slog.w(TAG, "Ignoring unregistering unknown router2"); + Slog.w( + TAG, + TextUtils.formatSimple( + "Ignoring unregistering unknown router: %s, died: %b", router, died)); return; } Slog.i( TAG, TextUtils.formatSimple( - "unregisterRouter2 | package: %s, router id: %d", - routerRecord.mPackageName, routerRecord.mRouterId)); + "unregisterRouter2 | package: %s, router id: %d, died: %b", + routerRecord.mPackageName, routerRecord.mRouterId, died)); UserRecord userRecord = routerRecord.mUserRecord; userRecord.mRouterRecords.remove(routerRecord); @@ -1161,6 +1171,7 @@ class MediaRouter2ServiceImpl { return sessionInfos; } + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) @GuardedBy("mLock") private void registerManagerLocked( @NonNull IMediaRouter2Manager manager, @@ -1184,8 +1195,7 @@ class MediaRouter2ServiceImpl { + " callerUserId: %d", callerUid, callerPid, callerPackageName, callerUserId)); - mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid, - "Must hold MEDIA_CONTENT_CONTROL permission."); + enforcePrivilegedRoutingPermissions(callerUid, callerPid); UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId); managerRecord = new ManagerRecord( @@ -1230,15 +1240,22 @@ class MediaRouter2ServiceImpl { private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) { ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder()); if (managerRecord == null) { + Slog.w( + TAG, + TextUtils.formatSimple( + "Ignoring unregistering unknown manager: %s, died: %b", manager, died)); return; } UserRecord userRecord = managerRecord.mUserRecord; - Slog.i(TAG, TextUtils.formatSimple( - "unregisterManager | package: %s, user: %d, manager: %d", - managerRecord.mOwnerPackageName, - userRecord.mUserId, - managerRecord.mManagerId)); + Slog.i( + TAG, + TextUtils.formatSimple( + "unregisterManager | package: %s, user: %d, manager: %d, died: %b", + managerRecord.mOwnerPackageName, + userRecord.mUserId, + managerRecord.mManagerId, + died)); userRecord.mManagerRecords.remove(managerRecord); managerRecord.dispose(); diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 44719f88351b..6df4a95f8b8c 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -409,6 +409,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub } // Binder call + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) @Override public boolean verifyPackageExists(String clientPackageName) { return mService2.verifyPackageExists(clientPackageName); @@ -536,6 +537,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub } // Binder call + @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL) @Override public void registerManager(IMediaRouter2Manager manager, String callerPackageName) { final int uid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java index 5bad06777328..6c74cba99bcb 100644 --- a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java +++ b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java @@ -21,8 +21,8 @@ import com.android.internal.util.FrameworkStatsLog; /** Wrapper around {@link FrameworkStatsLog} */ public class FrameworkStatsLogWrapper { - /** Wrapper around {@link FrameworkStatsLog#write}. */ - public void write( + /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionStateChanged atom. */ + public void writeStateChanged( int code, int sessionId, int state, @@ -41,4 +41,21 @@ public class FrameworkStatsLogWrapper { timeSinceLastActive, creationSource); } + + /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionTargetChanged atom. */ + public void writeTargetChanged( + int code, + int sessionId, + int targetType, + int hostUid, + int targetUid, + int windowingMode) { + FrameworkStatsLog.write( + code, + sessionId, + targetType, + hostUid, + targetUid, + windowingMode); + } } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 893ed6119f9f..6deda468f9a2 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -479,6 +479,18 @@ public final class MediaProjectionManagerService extends SystemService mMediaProjectionMetricsLogger.logAppSelectorDisplayed(hostUid); } + @VisibleForTesting + void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode) { + synchronized (mLock) { + if (mProjectionGrant == null) { + Slog.i(TAG, "Cannot log MediaProjectionTargetChanged atom due to null projection"); + } else { + mMediaProjectionMetricsLogger.logChangedWindowingMode( + contentToRecord, mProjectionGrant.uid, targetUid, windowingMode); + } + } + } + /** * Handles result of dialog shown from * {@link BinderService#buildReviewGrantedConsentIntentLocked()}. @@ -905,6 +917,20 @@ public final class MediaProjectionManagerService extends SystemService } @Override // Binder call + @EnforcePermission(MANAGE_MEDIA_PROJECTION) + public void notifyWindowingModeChanged( + int contentToRecord, int targetUid, int windowingMode) { + notifyWindowingModeChanged_enforcePermission(); + final long token = Binder.clearCallingIdentity(); + try { + MediaProjectionManagerService.this.notifyWindowingModeChanged( + contentToRecord, targetUid, windowingMode); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java index d7fefeb0b1fe..be2a25a755a5 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java @@ -16,16 +16,32 @@ package com.android.server.media.projection; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; +import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; + import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN; +import android.app.WindowConfiguration.WindowingMode; import android.content.Context; import android.util.Log; +import android.view.ContentRecordingSession.RecordContent; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import java.time.Duration; @@ -91,7 +107,7 @@ public class MediaProjectionMetricsLogger { durationSinceLastActiveSession == null ? TIME_SINCE_LAST_ACTIVE_UNKNOWN : (int) durationSinceLastActiveSession.toSeconds(); - write( + writeStateChanged( mSessionIdGenerator.createAndGetNewSessionId(), MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED, hostUid, @@ -102,13 +118,13 @@ public class MediaProjectionMetricsLogger { /** * Logs that the user entered the setup flow and permission dialog is displayed. This state is - * not sent when the permission is already granted and we skipped showing the permission dialog. + * not sent when the permission is already granted, and we skipped showing the permission dialog. * * @param hostUid UID of the package that initiates MediaProjection. */ public void logPermissionRequestDisplayed(int hostUid) { Log.d(TAG, "logPermissionRequestDisplayed"); - write( + writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED, hostUid, @@ -123,7 +139,7 @@ public class MediaProjectionMetricsLogger { * @param hostUid UID of the package that initiates MediaProjection. */ public void logProjectionPermissionRequestCancelled(int hostUid) { - write( + writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), FrameworkStatsLog .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED, @@ -141,7 +157,7 @@ public class MediaProjectionMetricsLogger { */ public void logAppSelectorDisplayed(int hostUid) { Log.d(TAG, "logAppSelectorDisplayed"); - write( + writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED, hostUid, @@ -158,7 +174,7 @@ public class MediaProjectionMetricsLogger { */ public void logInProgress(int hostUid, int targetUid) { Log.d(TAG, "logInProgress"); - write( + writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS, hostUid, @@ -168,6 +184,54 @@ public class MediaProjectionMetricsLogger { } /** + * Logs that the windowing mode of a projection has changed. + * + * @param contentToRecord ContentRecordingSession.RecordContent indicating whether it is a + * task capture or display capture - gets converted to the corresponding + * TargetType before being logged. + * @param hostUid UID of the package that initiates MediaProjection. + * @param targetUid UID of the package that is captured if selected. + * @param windowingMode Updated WindowConfiguration.WindowingMode of the captured region - gets + * converted to the corresponding TargetWindowingMode before being logged. + */ + public void logChangedWindowingMode( + int contentToRecord, int hostUid, int targetUid, int windowingMode) { + Log.d(TAG, "logChangedWindowingMode"); + writeTargetChanged( + mSessionIdGenerator.getCurrentSessionId(), + contentToRecordToTargetType(contentToRecord), + hostUid, + targetUid, + windowingModeToTargetWindowingMode(windowingMode)); + + } + + @VisibleForTesting + public int contentToRecordToTargetType(@RecordContent int recordContentType) { + return switch (recordContentType) { + case RECORD_CONTENT_DISPLAY -> + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY; + case RECORD_CONTENT_TASK -> + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK; + default -> MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN; + }; + } + + @VisibleForTesting + public int windowingModeToTargetWindowingMode(@WindowingMode int windowingMode) { + return switch (windowingMode) { + case WINDOWING_MODE_FULLSCREEN -> + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN; + case WINDOWING_MODE_FREEFORM -> + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM; + case WINDOWING_MODE_MULTI_WINDOW -> + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN; + default -> + MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN; + }; + } + + /** * Logs that the capturing stopped, either normally or because of error. * * @param hostUid UID of the package that initiates MediaProjection. @@ -178,7 +242,7 @@ public class MediaProjectionMetricsLogger { mPreviousState == MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS; Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress); - write( + writeStateChanged( mSessionIdGenerator.getCurrentSessionId(), MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED, hostUid, @@ -191,14 +255,31 @@ public class MediaProjectionMetricsLogger { } } - private void write( + public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) { + writeStateChanged(hostUid, state, sessionCreationSource); + } + + private void writeStateChanged(int hostUid, int state, int sessionCreationSource) { + mFrameworkStatsLogWrapper.writeStateChanged( + /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED, + /* session_id */ 123, + /* state */ state, + /* previous_state */ FrameworkStatsLog + .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN, + /* host_uid */ hostUid, + /* target_uid */ -1, + /* time_since_last_active */ 0, + /* creation_source */ sessionCreationSource); + } + + private void writeStateChanged( int sessionId, int state, int hostUid, int targetUid, int timeSinceLastActive, int creationSource) { - mFrameworkStatsLogWrapper.write( + mFrameworkStatsLogWrapper.writeStateChanged( /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED, sessionId, state, @@ -209,4 +290,19 @@ public class MediaProjectionMetricsLogger { creationSource); mPreviousState = state; } + + private void writeTargetChanged( + int sessionId, + int targetType, + int hostUid, + int targetUid, + int targetWindowingMode) { + mFrameworkStatsLogWrapper.writeTargetChanged( + /* code */ FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED, + sessionId, + targetType, + hostUid, + targetUid, + targetWindowingMode); + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4b5d52f0a8de..b2f00a246c23 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -291,6 +291,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.compat.IPlatformCompat; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; import com.android.internal.logging.InstanceId; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.MetricsLogger; @@ -2909,7 +2910,16 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.updateFixedImportance(mUm.getUsers()); mPreferencesHelper.migrateNotificationPermissions(mUm.getUsers()); } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { - if (expireBitmaps()) { + if (mFlagResolver.isEnabled(NotificationFlags.DEBUG_SHORT_BITMAP_DURATION)) { + new Thread(() -> { + while (true) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { } + mInternalService.removeBitmaps(); + } + }).start(); + } else if (expireBitmaps()) { NotificationBitmapJobService.scheduleJob(getContext()); } } @@ -6765,7 +6775,14 @@ public class NotificationManagerService extends SystemService { final long timePostedMs = r.getSbn().getPostTime(); final long timeNowMs = System.currentTimeMillis(); - if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) { + final long bitmapDuration; + if (mFlagResolver.isEnabled(NotificationFlags.DEBUG_SHORT_BITMAP_DURATION)) { + bitmapDuration = Duration.ofSeconds(5).toMillis(); + } else { + bitmapDuration = BITMAP_DURATION.toMillis(); + } + + if (isBitmapExpired(timePostedMs, timeNowMs, bitmapDuration)) { removeBitmapAndRepost(r); } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 2951ef630eb3..40d3ef03395d 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -1867,8 +1867,7 @@ final class InstallPackageHelper { final File targetDir = resolveTargetDir(request.getInstallFlags(), request.getCodeFile()); final File beforeCodeFile = request.getCodeFile(); - final File afterCodeFile = PackageManagerServiceUtils.getNextCodePath(targetDir, - parsedPackage.getPackageName()); + final File afterCodeFile = PackageManagerServiceUtils.getNextCodePath(targetDir); if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile); final boolean onIncremental = mPm.mIncrementalManager != null @@ -3099,8 +3098,7 @@ final class InstallPackageHelper { return null; } final File dstCodePath = - PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null), - packageName); + PackageManagerServiceUtils.getNextCodePath(Environment.getDataAppDirectory(null)); int ret = PackageManagerServiceUtils.decompressFiles(codePath, dstCodePath, packageName); if (ret == PackageManager.INSTALL_SUCCEEDED) { ret = PackageManagerServiceUtils.extractNativeBinaries(dstCodePath, packageName); diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index 42a97f7e05b4..5cd62872e07b 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -17,8 +17,8 @@ package com.android.server.pm; import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; -import static android.content.pm.ArchivedActivity.bytesFromBitmap; -import static android.content.pm.ArchivedActivity.drawableToBitmap; +import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap; +import static android.content.pm.ArchivedActivityInfo.drawableToBitmap; import static android.content.pm.PackageManager.DELETE_ARCHIVE; import static android.content.pm.PackageManager.DELETE_KEEP_DATA; import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE; @@ -46,6 +46,12 @@ import android.content.pm.ResolveInfo; import android.content.pm.VersionedPackage; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -56,6 +62,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.pkg.ArchiveState; @@ -367,7 +374,7 @@ public class PackageArchiver { // TODO(b/298452477) Handle monochrome icons. // In the rare case the archived app defined more than two launcher activities, we choose // the first one arbitrarily. - return decodeIcon(activityInfos.get(0)); + return includeCloudOverlay(decodeIcon(activityInfos.get(0))); } @VisibleForTesting @@ -375,6 +382,34 @@ public class PackageArchiver { return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString()); } + Bitmap includeCloudOverlay(Bitmap bitmap) { + Drawable cloudDrawable = + mContext.getResources() + .getDrawable(R.drawable.archived_app_cloud_overlay, mContext.getTheme()); + if (cloudDrawable == null) { + Slog.e(TAG, "Unable to locate cloud overlay for archived app!"); + return bitmap; + } + BitmapDrawable appIconDrawable = new BitmapDrawable(mContext.getResources(), bitmap); + PorterDuffColorFilter colorFilter = + new PorterDuffColorFilter( + Color.argb(0.32f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */), + PorterDuff.Mode.SRC_ATOP); + appIconDrawable.setColorFilter(colorFilter); + appIconDrawable.setBounds( + 0 /* left */, + 0 /* top */, + cloudDrawable.getIntrinsicWidth(), + cloudDrawable.getIntrinsicHeight()); + LayerDrawable layerDrawable = + new LayerDrawable(new Drawable[] {appIconDrawable, cloudDrawable}); + final int iconSize = mContext.getSystemService( + ActivityManager.class).getLauncherLargeIconSize(); + Bitmap appIconWithCloudOverlay = drawableToBitmap(layerDrawable, iconSize); + bitmap.recycle(); + return appIconWithCloudOverlay; + } + private void verifyArchived(PackageStateInternal ps, int userId) throws PackageManager.NameNotFoundException { PackageUserStateInternal userState = ps.getUserStateOrDefault(userId); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index bcb7bdebf9bf..f8e909e4c112 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -30,7 +30,6 @@ import static com.android.server.pm.PackageManagerService.COMPRESSED_EXTENSION; import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION; import static com.android.server.pm.PackageManagerService.DEBUG_INTENT_MATCHING; import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED; -import static com.android.server.pm.PackageManagerService.RANDOM_CODEPATH_PREFIX; import static com.android.server.pm.PackageManagerService.RANDOM_DIR_PREFIX; import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.STUB_SUFFIX; @@ -1299,16 +1298,15 @@ public class PackageManagerServiceUtils { } /** - * Given {@code targetDir}, returns {@code targetDir/~~[randomStrA]/[packageName]-[randomStrB].} + * Given {@code targetDir}, returns {@code targetDir/~~[randomStrA]/[randomStrB].} * Makes sure that {@code targetDir/~~[randomStrA]} directory doesn't exist. * Notice that this method doesn't actually create any directory. * * @param targetDir Directory that is two-levels up from the result directory. - * @param packageName Name of the package whose code files are to be installed under the result - * directory. - * @return File object for the directory that should hold the code files of {@code packageName}. + * + * @return File object for the directory that should hold the code files. */ - public static File getNextCodePath(File targetDir, String packageName) { + public static File getNextCodePath(File targetDir) { SecureRandom random = new SecureRandom(); byte[] bytes = new byte[16]; File firstLevelDir; @@ -1320,22 +1318,8 @@ public class PackageManagerServiceUtils { } while (firstLevelDir.exists()); random.nextBytes(bytes); - String dirName = packageName + RANDOM_CODEPATH_PREFIX + Base64.encodeToString(bytes, - Base64.URL_SAFE | Base64.NO_WRAP); - final File result = new File(firstLevelDir, dirName); - if (DEBUG && !Objects.equals(tryParsePackageName(result.getName()), packageName)) { - throw new RuntimeException( - "codepath is off: " + result.getName() + " (" + packageName + ")"); - } - return result; - } - - static String tryParsePackageName(@NonNull String codePath) throws IllegalArgumentException { - int packageNameEnds = codePath.indexOf(RANDOM_CODEPATH_PREFIX); - if (packageNameEnds == -1) { - throw new IllegalArgumentException("Not a valid package folder name"); - } - return codePath.substring(0, packageNameEnds); + String dirName = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP); + return new File(firstLevelDir, dirName); } /** diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 534f17601b86..c97fbdad9bf4 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3836,7 +3836,8 @@ public class UserManagerService extends IUserManager.Stub { if (type == XmlPullParser.START_TAG) { final String name = parser.getName(); if (name.equals(TAG_USER)) { - UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID)); + UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID), + mUserVersion); if (userData != null) { synchronized (mUsersLock) { @@ -4555,7 +4556,7 @@ public class UserManagerService extends IUserManager.Stub { } @GuardedBy({"mPackagesLock"}) - private UserData readUserLP(int id) { + private UserData readUserLP(int id, int userVersion) { try (ResilientAtomicFile file = getUserFile(id)) { FileInputStream fis = null; try { @@ -4564,19 +4565,19 @@ public class UserManagerService extends IUserManager.Stub { Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id); return null; } - return readUserLP(id, fis); + return readUserLP(id, fis, userVersion); } catch (Exception e) { // Remove corrupted file and retry. Slog.e(LOG_TAG, "Error reading user info, user id: " + id); file.failRead(fis, e); - return readUserLP(id); + return readUserLP(id, userVersion); } } } @GuardedBy({"mPackagesLock"}) @VisibleForTesting - UserData readUserLP(int id, InputStream is) throws IOException, + UserData readUserLP(int id, InputStream is, int userVersion) throws IOException, XmlPullParserException { int flags = 0; String userType = null; @@ -4669,7 +4670,17 @@ public class UserManagerService extends IUserManager.Stub { } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { legacyLocalRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS.equals(tag)) { - localRestrictions = UserRestrictionsUtils.readRestrictions(parser); + if (userVersion < 10) { + // Prior to version 10, the local user restrictions were stored as sub tags + // grouped by the user id of the source user. The source is no longer stored + // on versions 10+ as this is now stored in the DevicePolicyEngine. + RestrictionsSet oldLocalRestrictions = + RestrictionsSet.readRestrictions( + parser, TAG_DEVICE_POLICY_LOCAL_RESTRICTIONS); + localRestrictions = oldLocalRestrictions.mergeAll(); + } else { + localRestrictions = UserRestrictionsUtils.readRestrictions(parser); + } } else if (TAG_DEVICE_POLICY_GLOBAL_RESTRICTIONS.equals(tag)) { globalRestrictions = UserRestrictionsUtils.readRestrictions(parser); } else if (TAG_GUEST_RESTRICTIONS.equals(tag)) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 077812b49cdd..45ca690ba20f 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -19,6 +19,7 @@ package com.android.server.policy; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.Manifest.permission.SYSTEM_APPLICATION_OVERLAY; +import static android.app.AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE; @@ -31,6 +32,7 @@ import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.O; import static android.os.IInputConstants.INVALID_INPUT_DEVICE_ID; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; +import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.STATE_OFF; @@ -2992,12 +2994,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Window manager does the checking for this. outAppOp[0] = OP_TOAST_WINDOW; return ADD_OKAY; + case TYPE_ACCESSIBILITY_OVERLAY: + if (createAccessibilityOverlayAppOpEnabled()) { + outAppOp[0] = OP_CREATE_ACCESSIBILITY_OVERLAY; + return ADD_OKAY; + } case TYPE_INPUT_METHOD: case TYPE_WALLPAPER: case TYPE_PRESENTATION: case TYPE_PRIVATE_PRESENTATION: case TYPE_VOICE_INTERACTION: - case TYPE_ACCESSIBILITY_OVERLAY: case TYPE_QS_DIALOG: case TYPE_NAVIGATION_BAR_PANEL: // The window manager will check these. diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 3c8e630dba2b..26f0d34a6261 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1585,7 +1585,7 @@ class ActivityClientController extends IActivityClientController.Stub { * the Activities in the Task should be finished when it finishes. Otherwise, return {@code * false}. */ - private boolean isRelativeTaskRootActivity(ActivityRecord r, ActivityRecord taskRoot) { + private static boolean isRelativeTaskRootActivity(ActivityRecord r, ActivityRecord taskRoot) { // Not a relative root if the given Activity is not the root Activity of its TaskFragment. final TaskFragment taskFragment = r.getTaskFragment(); if (r != taskFragment.getActivity(ar -> !ar.finishing || ar == r, @@ -1598,7 +1598,7 @@ class ActivityClientController extends IActivityClientController.Stub { return taskRoot.getTaskFragment().getCompanionTaskFragment() == taskFragment; } - private boolean isTopActivityInTaskFragment(ActivityRecord activity) { + private static boolean isTopActivityInTaskFragment(ActivityRecord activity) { return activity.getTaskFragment().topRunningActivity() == activity; } @@ -1614,9 +1614,6 @@ class ActivityClientController extends IActivityClientController.Stub { public void onBackPressed(IBinder token, IRequestFinishCallback callback) { final long origId = Binder.clearCallingIdentity(); try { - final Intent baseActivityIntent; - final boolean launchedFromHome; - final boolean isLastRunningActivity; synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); if (r == null) return; @@ -1624,39 +1621,16 @@ class ActivityClientController extends IActivityClientController.Stub { final Task task = r.getTask(); final ActivityRecord root = task.getRootActivity(false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/); - final boolean isTaskRoot = r == root; - if (isTaskRoot) { - if (mService.mWindowOrganizerController.mTaskOrganizerController + if (r == root && mService.mWindowOrganizerController.mTaskOrganizerController .handleInterceptBackPressedOnTaskRoot(r.getRootTask())) { - // This task is handled by a task organizer that has requested the back - // pressed callback. - return; - } - } else if (!isRelativeTaskRootActivity(r, root)) { - // Finish the Activity if the activity is not the task root or relative root. - requestCallbackFinish(callback); + // This task is handled by a task organizer that has requested the back + // pressed callback. + return; + } + if (shouldMoveTaskToBack(r, root)) { + moveActivityTaskToBack(token, true /* nonRoot */); return; } - - isLastRunningActivity = isTopActivityInTaskFragment(isTaskRoot ? root : r); - - final boolean isBaseActivity = root.mActivityComponent.equals(task.realActivity); - baseActivityIntent = isBaseActivity ? root.intent : null; - - launchedFromHome = root.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME); - } - - // If the activity was launched directly from the home screen, then we should - // refrain from finishing the activity and instead move it to the back to keep it in - // memory. The requirements for this are: - // 1. The activity is the last running activity in the task. - // 2. The current activity is the base activity for the task. - // 3. The activity was launched by the home process, and is one of the main entry - // points for the application. - if (baseActivityIntent != null && isLastRunningActivity - && launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent)) { - moveActivityTaskToBack(token, true /* nonRoot */); - return; } // The default option for handling the back button is to finish the Activity. @@ -1666,6 +1640,27 @@ class ActivityClientController extends IActivityClientController.Stub { } } + static boolean shouldMoveTaskToBack(ActivityRecord r, ActivityRecord rootActivity) { + if (r != rootActivity && !isRelativeTaskRootActivity(r, rootActivity)) { + return false; + } + final boolean isBaseActivity = rootActivity.mActivityComponent.equals( + r.getTask().realActivity); + final Intent baseActivityIntent = isBaseActivity ? rootActivity.intent : null; + + // If the activity was launched directly from the home screen, then we should + // refrain from finishing the activity and instead move it to the back to keep it in + // memory. The requirements for this are: + // 1. The activity is the last running activity in the task. + // 2. The current activity is the base activity for the task. + // 3. The activity was launched by the home process, and is one of the main entry + // points for the application. + return baseActivityIntent != null + && isTopActivityInTaskFragment(r) + && rootActivity.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME) + && ActivityRecord.isMainIntent(baseActivityIntent); + } + @Override public void enableTaskLocaleOverride(IBinder token) { if (UserHandle.getAppId(Binder.getCallingUid()) != SYSTEM_UID) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index bdab4d483872..b70fa8ff7596 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2887,7 +2887,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final StartingSurfaceController.StartingSurface surface; - final WindowState startingWindow = mStartingWindow; final boolean animate; if (mStartingData != null) { if (mStartingData.mWaitForSyncTransactionCommit @@ -4545,7 +4544,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; } // Post cleanup after the visibility and animation are transferred. - fromActivity.postWindowRemoveStartingWindowCleanup(); + fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow); fromActivity.mVisibleSetFromTransferredStartingWindow = false; mWmService.updateFocusedWindowLocked( @@ -7461,7 +7460,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - void postWindowRemoveStartingWindowCleanup() { + void postWindowRemoveStartingWindowCleanup(@NonNull WindowState win) { + if (mStartingWindow == win) { + // This could only happen when the window is removed from hierarchy. So do not keep its + // reference anymore. + mStartingWindow = null; + } if (mChildren.size() == 0 && mVisibleSetFromTransferredStartingWindow) { // We set the visible state to true for the token from a transferred starting // window. We now reset it back to false since the starting window was the last diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d1728d6b55e0..2d37b9b2745d 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -43,6 +43,7 @@ import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -64,6 +65,7 @@ import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; /** @@ -141,10 +143,6 @@ class BackNavigationController { // multiple Activities in the Stack. Task prevTask = null; - // The previous activity we're going back to. This can be either a child of currentTask - // if there are more than one Activity in currentTask, or a child of prevTask, if - // currentActivity is the last child of currentTask. - ActivityRecord prevActivity; WindowContainer<?> removedWindowContainer = null; WindowState window; @@ -264,14 +262,21 @@ class BackNavigationController { return infoBuilder.build(); } + // The previous activity we're going back to. This can be either a child of currentTask + // if there are more than one Activity in currentTask, or a child of prevTask, if + // currentActivity is the last child of currentTask. // We don't have an application callback, let's find the destination of the back gesture // The search logic should align with ActivityClientController#finishActivity - prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); + final ArrayList<ActivityRecord> prevActivities = new ArrayList<>(); + final boolean canAnimate = getAnimatablePrevActivities(currentTask, currentActivity, + prevActivities); final boolean isOccluded = isKeyguardOccluded(window); - // TODO Dialog window does not need to attach on activity, check - // window.mAttrs.type != TYPE_BASE_APPLICATION - if ((window.getParent().getChildCount() > 1 + if (!canAnimate) { + backType = BackNavigationInfo.TYPE_CALLBACK; + } else if ((window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0) != window)) { + // TODO Dialog window does not need to attach on activity, check + // window.mAttrs.type != TYPE_BASE_APPLICATION // Are we the top window of our parent? If not, we are a window on top of the // activity, we won't close the activity. backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; @@ -279,9 +284,8 @@ class BackNavigationController { } else if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) { // skip if current activity is translucent backType = BackNavigationInfo.TYPE_CALLBACK; - removedWindowContainer = window; - } else if (prevActivity != null) { - if (!isOccluded || prevActivity.canShowWhenLocked()) { + } else if (prevActivities.size() > 0) { + if (!isOccluded || prevActivities.get(0).canShowWhenLocked()) { // We have another Activity in the same currentTask to go to final WindowContainer parent = currentActivity.getParent(); final boolean canCustomize = parent != null @@ -303,40 +307,45 @@ class BackNavigationController { } } removedWindowContainer = currentActivity; - prevTask = prevActivity.getTask(); + prevTask = prevActivities.get(0).getTask(); backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else { + // keyguard locked and activities are unable to show when locked. backType = BackNavigationInfo.TYPE_CALLBACK; } - } else if (currentTask.returnsToHomeRootTask()) { - if (isOccluded) { + } else { + // TODO(208789724): Create single source of truth for this, maybe in + // RootWindowContainer + prevTask = currentTask.mRootWindowContainer.getTask(t -> { + if (t.showToCurrentUser() && !t.mChildren.isEmpty()) { + final ActivityRecord ar = t.getTopNonFinishingActivity(); + return ar != null && ar.showToCurrentUser(); + } + return false; + }, currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); + final ActivityRecord tmpPre = prevTask.getTopNonFinishingActivity(); + if (tmpPre != null) { + prevActivities.add(tmpPre); + findAdjacentActivityIfExist(tmpPre, prevActivities); + } + if (prevActivities.isEmpty() + || (isOccluded && !prevActivities.get(0).canShowWhenLocked())) { backType = BackNavigationInfo.TYPE_CALLBACK; - } else { - // Our Task should bring back to home + } else if (prevTask.isActivityTypeHome()) { removedWindowContainer = currentTask; - prevTask = currentTask.getDisplayArea().getRootHomeTask(); backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; mShowWallpaper = true; - } - } else if (currentActivity.isRootOfTask()) { - // TODO(208789724): Create single source of truth for this, maybe in - // RootWindowContainer - prevTask = currentTask.mRootWindowContainer.getTask(Task::showToCurrentUser, - currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); - removedWindowContainer = currentTask; - // If it reaches the top activity, we will check the below task from parent. - // If it's null or multi-window, fallback the type to TYPE_CALLBACK. - // or set the type to proper value when it's return to home or another task. - if (prevTask == null || prevTask.inMultiWindowMode()) { - backType = BackNavigationInfo.TYPE_CALLBACK; } else { - prevActivity = prevTask.getTopNonFinishingActivity(); - if (prevActivity == null || (isOccluded && !prevActivity.canShowWhenLocked())) { + // If it reaches the top activity, we will check the below task from parent. + // If it's null or multi-window and has different parent task, fallback the type + // to TYPE_CALLBACK. Or set the type to proper value when it's return to home or + // another task. + final Task prevParent = prevTask.getParent().asTask(); + final Task currParent = currentTask.getParent().asTask(); + if (prevTask.inMultiWindowMode() && prevParent != currParent) { backType = BackNavigationInfo.TYPE_CALLBACK; - } else if (prevTask.isActivityTypeHome()) { - backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; - mShowWallpaper = true; } else { + removedWindowContainer = prevTask; backType = BackNavigationInfo.TYPE_CROSS_TASK; } } @@ -345,7 +354,8 @@ class BackNavigationController { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " + "removedContainer:%s, backType=%s", - prevActivity != null ? prevActivity.mActivityComponent : null, + prevActivities.size() > 0 ? TextUtils.join(";", prevActivities.stream() + .map(r -> r.mActivityComponent).toArray()) : null, prevTask != null ? prevTask.getName() : null, removedWindowContainer, BackNavigationInfo.typeToString(backType)); @@ -360,7 +370,7 @@ class BackNavigationController { if (prepareAnimation) { final AnimationHandler.ScheduleAnimationBuilder builder = mAnimationHandler.prepareAnimation(backType, adapter, - currentTask, prevTask, currentActivity, prevActivity); + currentTask, prevTask, currentActivity, prevActivities); mBackAnimationInProgress = builder != null; if (mBackAnimationInProgress) { if (removedWindowContainer.hasCommittedReparentToAnimationLeash() @@ -372,8 +382,8 @@ class BackNavigationController { // Current transition is still running, we have to defer the hiding to the // client process to prevent the unexpected relayout when handling the back // animation. - if (prevActivity != null) { - prevActivity.setDeferHidingClient(true); + for (int i = prevActivities.size() - 1; i >= 0; --i) { + prevActivities.get(i).setDeferHidingClient(true); } } else { scheduleAnimation(builder); @@ -381,17 +391,91 @@ class BackNavigationController { } } infoBuilder.setPrepareRemoteAnimation(prepareAnimation); - } // Release wm Lock - WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; - if (finalRemovedWindowContainer != null) { - final int finalBackType = backType; - RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( - result, finalBackType)); - infoBuilder.setOnBackNavigationDone(onBackNavigationDone); + if (removedWindowContainer != null) { + final int finalBackType = backType; + final RemoteCallback onBackNavigationDone = new RemoteCallback(result -> + onBackNavigationDone(result, finalBackType)); + infoBuilder.setOnBackNavigationDone(onBackNavigationDone); + } else { + mNavigationMonitor.stopMonitorForRemote(); + } + mLastBackType = backType; + return infoBuilder.build(); } - mLastBackType = backType; - return infoBuilder.build(); + } + + /** + * Gets previous activities from currentActivity. + * + * @return false if unable to predict what will happen + */ + private static boolean getAnimatablePrevActivities(@NonNull Task currentTask, + @NonNull ActivityRecord currentActivity, + @NonNull ArrayList<ActivityRecord> outPrevActivities) { + if (currentActivity.mAtmService + .mTaskOrganizerController.shouldInterceptBackPressedOnRootTask( + currentTask.getRootTask())) { + // The task organizer will handle back pressed, don't play animation. + return false; + } + final ActivityRecord root = currentTask.getRootActivity(false /*ignoreRelinquishIdentity*/, + true /*setToBottomIfNone*/); + if (root != null && ActivityClientController.shouldMoveTaskToBack(currentActivity, root)) { + return true; + } + + // Searching previous + final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing, + currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/); + if (prevActivity == null) { + // No previous activity in this task, can still predict if previous task exists. + return true; + } + if (currentTask.getActivity((above) -> !above.finishing, currentActivity, + false /*includeBoundary*/, false /*traverseTopToBottom*/) != null) { + // another activity is above this activity, don't know what will happen + return false; + } + + final TaskFragment currTF = currentActivity.getTaskFragment(); + final TaskFragment prevTF = prevActivity.getTaskFragment(); + if (currTF != prevTF && prevTF != null) { + final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment(); + if (prevTFAdjacent != null) { + if (prevTFAdjacent == currTF) { + // Cannot predict what will happen when app receive back key, skip animation. + outPrevActivities.clear(); + return false; + } else { + final ActivityRecord prevActivityAdjacent = + prevTFAdjacent.getTopNonFinishingActivity(); + if (prevActivityAdjacent != null) { + outPrevActivities.add(prevActivityAdjacent); + } else { + // Don't know what will happen. + outPrevActivities.clear(); + return false; + } + } + } + } + outPrevActivities.add(prevActivity); + return true; + } + + private static void findAdjacentActivityIfExist(@NonNull ActivityRecord mainActivity, + @NonNull ArrayList<ActivityRecord> outList) { + final TaskFragment mainTF = mainActivity.getTaskFragment(); + if (mainTF == null || mainTF.getAdjacentTaskFragment() == null) { + return; + } + final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment(); + final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity(); + if (topActivity == null) { + return; + } + outList.add(topActivity); } boolean isMonitoringTransition() { @@ -696,7 +780,7 @@ class BackNavigationController { if (!hasTarget) { // Skip if no target participated in current finished transition. Slog.w(TAG, "Finished transition didn't include the targets" - + " open: " + mPendingAnimationBuilder.mOpenTarget + + " open: " + Arrays.toString(mPendingAnimationBuilder.mOpenTargets) + " close: " + mPendingAnimationBuilder.mCloseTarget); cancelPendingAnimation(); return false; @@ -732,7 +816,7 @@ class BackNavigationController { private final boolean mShowWindowlessSurface; private final WindowManagerService mWindowManagerService; private BackWindowAnimationAdaptor mCloseAdaptor; - private BackWindowAnimationAdaptor mOpenAdaptor; + private BackWindowAnimationAdaptorWrapper mOpenAnimAdaptor; private boolean mComposed; private boolean mWaitTransition; private int mSwitchType = UNKNOWN; @@ -741,7 +825,7 @@ class BackNavigationController { // exactly match animating target. When target match, reparent the starting surface to // the opening target like starting window do. private boolean mStartingSurfaceTargetMatch; - private ActivityRecord mOpenActivity; + private ActivityRecord[] mOpenActivities; AnimationHandler(WindowManagerService wms) { mWindowManagerService = wms; @@ -753,61 +837,69 @@ class BackNavigationController { private static final int TASK_SWITCH = 1; private static final int ACTIVITY_SWITCH = 2; - private static boolean isActivitySwitch(WindowContainer close, WindowContainer open) { - if (close.asActivityRecord() == null || open.asActivityRecord() == null - || (close.asActivityRecord().getTask() - != open.asActivityRecord().getTask())) { + private static boolean isActivitySwitch(@NonNull WindowContainer close, + @NonNull WindowContainer[] open) { + if (open == null || open.length == 0 || close.asActivityRecord() == null) { return false; } + final Task closeTask = close.asActivityRecord().getTask(); + for (int i = open.length - 1; i >= 0; --i) { + if (open[i].asActivityRecord() == null + || (closeTask != open[i].asActivityRecord().getTask())) { + return false; + } + } return true; } - private static boolean isTaskSwitch(WindowContainer close, WindowContainer open) { - if (close.asTask() == null || open.asTask() == null - || (close.asTask() == open.asTask())) { + private static boolean isTaskSwitch(@NonNull WindowContainer close, + @NonNull WindowContainer[] open) { + if (open == null || open.length != 1 || close.asTask() == null) { return false; } - return true; + return open[0].asTask() != null && (close.asTask() != open[0].asTask()); } - private void initiate(WindowContainer close, WindowContainer open, - ActivityRecord openActivity) { - WindowContainer closeTarget; + private void initiate(@NonNull WindowContainer close, @NonNull WindowContainer[] open, + @NonNull ActivityRecord[] openingActivities) { if (isActivitySwitch(close, open)) { mSwitchType = ACTIVITY_SWITCH; - closeTarget = close.asActivityRecord(); } else if (isTaskSwitch(close, open)) { mSwitchType = TASK_SWITCH; - // The transition target must be activity in legacy transition. - closeTarget = WindowManagerService.sEnableShellTransitions ? close - : close.asTask().getTopNonFinishingActivity(); } else { mSwitchType = UNKNOWN; return; } - mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType); - mOpenAdaptor = createAdaptor(open, true, mSwitchType); - mOpenActivity = openActivity; - if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) { + mCloseAdaptor = createAdaptor(close, false, mSwitchType); + if (mCloseAdaptor.mAnimationTarget == null) { Slog.w(TAG, "composeNewAnimations fail, skip"); clearBackAnimateTarget(); + return; } + + mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open); + if (!mOpenAnimAdaptor.isValid()) { + Slog.w(TAG, "compose animations fail, skip"); + clearBackAnimateTarget(); + return; + } + mOpenActivities = openingActivities; } private boolean composeAnimations(@NonNull WindowContainer close, - @NonNull WindowContainer open, ActivityRecord openActivity) { + @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities) { if (mComposed || mWaitTransition) { Slog.e(TAG, "Previous animation is running " + this); return false; } clearBackAnimateTarget(); - if (close == null || open == null || openActivity == null) { + if (close == null || open == null || open.length == 0 || open.length > 2) { Slog.e(TAG, "reset animation with null target close: " - + close + " open: " + open); + + close + " open: " + Arrays.toString(open)); return false; } - initiate(close, open, openActivity); + initiate(close, open, openingActivities); if (mSwitchType == UNKNOWN) { return false; } @@ -816,9 +908,14 @@ class BackNavigationController { return true; } - RemoteAnimationTarget[] getAnimationTargets() { - return mComposed ? new RemoteAnimationTarget[] { - mCloseAdaptor.mAnimationTarget, mOpenAdaptor.mAnimationTarget} : null; + @Nullable RemoteAnimationTarget[] getAnimationTargets() { + if (!mComposed) { + return null; + } + final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2]; + targets[0] = mCloseAdaptor.mAnimationTarget; + targets[1] = mOpenAnimAdaptor.getOrCreateAnimationTarget(); + return targets; } boolean isSupportWindowlessSurface() { @@ -826,7 +923,7 @@ class BackNavigationController { .isSupportWindowlessStartingSurface(); } - boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) { + boolean containTarget(@NonNull ArrayList<WindowContainer> wcs, boolean open) { for (int i = wcs.size() - 1; i >= 0; --i) { if (isTarget(wcs.get(i), open)) { return true; @@ -835,22 +932,35 @@ class BackNavigationController { return wcs.isEmpty(); } - boolean isTarget(WindowContainer wc, boolean open) { + boolean isTarget(@NonNull WindowContainer wc, boolean open) { if (!mComposed) { return false; } + if (open) { + for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { + if (isAnimateTarget(wc, mOpenAnimAdaptor.mAdaptors[i].mTarget, mSwitchType)) { + return true; + } + } + return false; + } + return isAnimateTarget(wc, mCloseAdaptor.mTarget, mSwitchType); + } - // WC must be ActivityRecord in legacy transition, but it also can be Task or - // TaskFragment when using Shell transition. - // Open target: Can be Task or ActivityRecord or TaskFragment - // Close target: Limit to the top activity for now, to reduce the chance of misjudgment. - final WindowContainer target = open ? mOpenAdaptor.mTarget : mCloseAdaptor.mTarget; - if (mSwitchType == TASK_SWITCH) { - return wc == target - || (wc.asTask() != null && wc.hasChild(target)) - || (wc.asActivityRecord() != null && target.hasChild(wc)); - } else if (mSwitchType == ACTIVITY_SWITCH) { - return wc == target || (wc.asTaskFragment() != null && wc.hasChild(target)); + private static boolean isAnimateTarget(@NonNull WindowContainer window, + @NonNull WindowContainer animationTarget, int switchType) { + if (switchType == TASK_SWITCH) { + // simplify home search for multiple hierarchy + if (window.isActivityTypeHome() && animationTarget.isActivityTypeHome()) { + return true; + } + return window == animationTarget + || (animationTarget.asTask() != null && animationTarget.hasChild(window)) + || (animationTarget.asActivityRecord() != null + && window.hasChild(animationTarget)); + } else if (switchType == ACTIVITY_SWITCH) { + return window == animationTarget + || (window.asTaskFragment() != null && window.hasChild(animationTarget)); } return false; } @@ -864,19 +974,25 @@ class BackNavigationController { mCloseAdaptor.mTarget.cancelAnimation(); mCloseAdaptor = null; } - if (mOpenAdaptor != null) { - mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch); - mOpenAdaptor.mTarget.cancelAnimation(); - mOpenAdaptor = null; + if (mOpenAnimAdaptor != null) { + mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch); + mOpenAnimAdaptor = null; } - if (mOpenActivity != null && mOpenActivity.mLaunchTaskBehind) { - restoreLaunchBehind(mOpenActivity); + + if (mOpenActivities != null) { + for (int i = mOpenActivities.length - 1; i >= 0; --i) { + if (mOpenActivities[i].mLaunchTaskBehind) { + restoreLaunchBehind(mOpenActivities[i]); + } + } } } void markStartingSurfaceMatch() { mStartingSurfaceTargetMatch = true; - mOpenAdaptor.reparentWindowlessSurfaceToTarget(); + for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { + mOpenAnimAdaptor.mAdaptors[i].reparentWindowlessSurfaceToTarget(); + } } void clearBackAnimateTarget() { @@ -885,13 +1001,13 @@ class BackNavigationController { mWaitTransition = false; mStartingSurfaceTargetMatch = false; mSwitchType = UNKNOWN; - mOpenActivity = null; + mOpenActivities = null; } // The close target must in close list // The open target can either in close or open list - boolean containsBackAnimationTargets(ArrayList<WindowContainer> openApps, - ArrayList<WindowContainer> closeApps) { + boolean containsBackAnimationTargets(@NonNull ArrayList<WindowContainer> openApps, + @NonNull ArrayList<WindowContainer> closeApps) { return containTarget(closeApps, false /* open */) && (containTarget(openApps, true /* open */) || containTarget(openApps, false /* open */)); @@ -901,9 +1017,9 @@ class BackNavigationController { public String toString() { return "AnimationTargets{" + " openTarget= " - + (mOpenAdaptor != null ? mOpenAdaptor.mTarget : "null") + + (mOpenAnimAdaptor != null ? dumpOpenAnimTargetsToString() : null) + " closeTarget= " - + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : "null") + + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : null) + " mSwitchType= " + mSwitchType + " mComposed= " @@ -913,8 +1029,21 @@ class BackNavigationController { + '}'; } - private static BackWindowAnimationAdaptor createAdaptor( - WindowContainer target, boolean isOpen, int switchType) { + private String dumpOpenAnimTargetsToString() { + final StringBuilder sb = new StringBuilder(); + sb.append("{"); + for (int i = 0; i < mOpenAnimAdaptor.mAdaptors.length; i++) { + if (i > 0) { + sb.append(','); + } + sb.append(mOpenAnimAdaptor.mAdaptors[i].mTarget); + } + sb.append("}"); + return sb.toString(); + } + + @NonNull private static BackWindowAnimationAdaptor createAdaptor( + @NonNull WindowContainer target, boolean isOpen, int switchType) { final BackWindowAnimationAdaptor adaptor = new BackWindowAnimationAdaptor(target, isOpen, switchType); final SurfaceControl.Transaction pt = target.getPendingTransaction(); @@ -930,6 +1059,100 @@ class BackNavigationController { return adaptor; } + private static class BackWindowAnimationAdaptorWrapper { + final BackWindowAnimationAdaptor[] mAdaptors; + SurfaceControl.Transaction mCloseTransaction; + + BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType, + @NonNull WindowContainer... targets) { + mAdaptors = new BackWindowAnimationAdaptor[targets.length]; + for (int i = targets.length - 1; i >= 0; --i) { + mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType); + } + } + + boolean isValid() { + for (int i = mAdaptors.length - 1; i >= 0; --i) { + if (mAdaptors[i].mAnimationTarget == null) { + return false; + } + } + return true; + } + + void cleanUp(boolean startingSurfaceMatch) { + for (int i = mAdaptors.length - 1; i >= 0; --i) { + mAdaptors[i].cleanUpWindowlessSurface(startingSurfaceMatch); + mAdaptors[i].mTarget.cancelAnimation(); + } + if (mCloseTransaction != null) { + mCloseTransaction.apply(); + mCloseTransaction = null; + } + } + + void onAnimationFinish() { + final SurfaceControl.Transaction pt = mAdaptors[0].mTarget.getPendingTransaction(); + if (mCloseTransaction != null) { + pt.merge(mCloseTransaction); + mCloseTransaction = null; + } + if (mAdaptors.length > 1) { + for (int i = mAdaptors.length - 1; i >= 0; --i) { + final WindowContainer wc = mAdaptors[i].mTarget; + final WindowContainer parent = wc.getParent(); + if (parent != null) { + pt.reparent(wc.getSurfaceControl(), + parent.getSurfaceControl()); + } + } + } + } + + @NonNull RemoteAnimationTarget getOrCreateAnimationTarget() { + // Special handle for opening two activities together. + // If we animate both activities separately, the animation area and rounded corner + // would also being handled separately. To make them seem like "open" together, wrap + // their leash with another animation leash. + if (mAdaptors.length > 1 && mCloseTransaction == null) { + final Rect unionBounds = new Rect(); + for (int i = mAdaptors.length - 1; i >= 0; --i) { + unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds); + } + final WindowContainer wc = mAdaptors[0].mTarget; + final Task task = wc.asActivityRecord() != null + ? wc.asActivityRecord().getTask() : wc.asTask(); + final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget; + final SurfaceControl leashSurface = new SurfaceControl.Builder() + .setName("cross-animation-leash") + .setContainerLayer() + .setHidden(false) + .setParent(task.getSurfaceControl()) + .build(); + final SurfaceControl.Transaction pt = wc.getPendingTransaction(); + pt.setLayer(leashSurface, wc.getParent().getLastLayer()); + mCloseTransaction = new SurfaceControl.Transaction(); + mCloseTransaction.reparent(leashSurface, null); + for (int i = mAdaptors.length - 1; i >= 0; --i) { + BackWindowAnimationAdaptor adaptor = mAdaptors[i]; + pt.reparent(adaptor.mAnimationTarget.leash, leashSurface); + pt.setPosition(adaptor.mAnimationTarget.leash, + adaptor.mAnimationTarget.localBounds.left, + adaptor.mAnimationTarget.localBounds.top); + } + return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface, + represent.isTranslucent, represent.clipRect, represent.contentInsets, + represent.prefixOrderIndex, + new Point(unionBounds.left, unionBounds.top), + unionBounds, unionBounds, represent.windowConfiguration, + true /* isNotInRecents */, null, null, represent.taskInfo, + represent.allowEnterPip); + } else { + return mAdaptors[0].mAnimationTarget; + } + } + } + private static class BackWindowAnimationAdaptor implements AnimationAdapter { SurfaceControl mCapturedLeash; private final Rect mBounds = new Rect(); @@ -943,7 +1166,7 @@ class BackNavigationController { private int mRequestedStartingSurfaceId = INVALID_TASK_ID; private SurfaceControl mStartingSurface; - BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen, + BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen, int switchType) { mBounds.set(target.getBounds()); mTarget = target; @@ -1034,7 +1257,7 @@ class BackNavigationController { return mAnimationTarget; } - void createStartingSurface() { + void createStartingSurface(@NonNull WindowContainer closeWindow) { if (!mIsOpen) { return; } @@ -1053,7 +1276,10 @@ class BackNavigationController { final TaskSnapshot snapshot = getSnapshot(mTarget); mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController .addWindowlessStartingSurface(openTask, mainActivity, - mAnimationTarget.leash, snapshot, + // Choose configuration from closeWindow, because the configuration + // of opening target may not update before resume, so the starting + // surface should occlude it entirely. + mAnimationTarget.leash, snapshot, closeWindow.getConfiguration(), new IWindowlessStartingSurfaceCallback.Stub() { // Once the starting surface has been created in shell, it will call // onSurfaceAdded to pass the created surface to core, so if a @@ -1108,15 +1334,17 @@ class BackNavigationController { ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter, Task currentTask, Task previousTask, ActivityRecord currentActivity, - ActivityRecord previousActivity) { + ArrayList<ActivityRecord> previousActivity) { switch (backType) { case BackNavigationInfo.TYPE_RETURN_TO_HOME: return new ScheduleAnimationBuilder(backType, adapter) .setIsLaunchBehind(true) .setComposeTarget(currentTask, previousTask); case BackNavigationInfo.TYPE_CROSS_ACTIVITY: + ActivityRecord[] prevActs = new ActivityRecord[previousActivity.size()]; + prevActs = previousActivity.toArray(prevActs); return new ScheduleAnimationBuilder(backType, adapter) - .setComposeTarget(currentActivity, previousActivity) + .setComposeTarget(currentActivity, prevActs) .setIsLaunchBehind(false); case BackNavigationInfo.TYPE_CROSS_TASK: return new ScheduleAnimationBuilder(backType, adapter) @@ -1130,7 +1358,7 @@ class BackNavigationController { final int mType; final BackAnimationAdapter mBackAnimationAdapter; WindowContainer mCloseTarget; - WindowContainer mOpenTarget; + WindowContainer[] mOpenTargets; boolean mIsLaunchBehind; ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) { @@ -1138,9 +1366,10 @@ class BackNavigationController { mBackAnimationAdapter = backAnimationAdapter; } - ScheduleAnimationBuilder setComposeTarget(WindowContainer close, WindowContainer open) { + ScheduleAnimationBuilder setComposeTarget(@NonNull WindowContainer close, + @NonNull WindowContainer... open) { mCloseTarget = close; - mOpenTarget = open; + mOpenTargets = open; return this; } @@ -1150,43 +1379,60 @@ class BackNavigationController { } boolean containTarget(@NonNull WindowContainer wc) { - return wc == mOpenTarget || wc == mCloseTarget - || mOpenTarget.hasChild(wc) || mCloseTarget.hasChild(wc); + if (mOpenTargets != null) { + for (int i = mOpenTargets.length - 1; i >= 0; --i) { + if (wc == mOpenTargets[i] || mOpenTargets[i].hasChild(wc)) { + return true; + } + } + } + return wc == mCloseTarget || mCloseTarget.hasChild(wc); } /** * Apply preview strategy on the opening target + * @param closeWindow The close window, where it's configuration should cover all + * open target(s). * @param openAnimationAdaptor The animator who can create starting surface. - * @param visibleOpenActivity The visible activity in opening target. + * @param visibleOpenActivities The visible activities in opening targets. */ - private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor, - ActivityRecord visibleOpenActivity) { - if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) { - openAnimationAdaptor.createStartingSurface(); - return; + private void applyPreviewStrategy(@NonNull WindowContainer closeWindow, + @NonNull BackWindowAnimationAdaptor[] openAnimationAdaptor, + @NonNull ActivityRecord[] visibleOpenActivities) { + if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind + // TODO (b/274997067) Draw two snapshot in a single starting surface. + // We are using TaskId as the key of + // StartingSurfaceDrawer#StartingWindowRecordManager, so we cannot create + // two activity snapshot with WindowlessStartingWindow. + // Try to draw two snapshot within a WindowlessStartingWindow, or find + // another key for StartingWindowRecordManager. + && openAnimationAdaptor.length == 1) { + openAnimationAdaptor[0].createStartingSurface(closeWindow); + } else { + for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { + setLaunchBehind(visibleOpenActivities[i]); + } } - setLaunchBehind(visibleOpenActivity); } - Runnable build() { - if (mOpenTarget == null || mCloseTarget == null) { + @Nullable Runnable build() { + if (mOpenTargets == null || mCloseTarget == null || mOpenTargets.length == 0) { return null; } - final ActivityRecord openActivity = mOpenTarget.asTask() != null - ? mOpenTarget.asTask().getTopNonFinishingActivity() - : mOpenTarget.asActivityRecord() != null - ? mOpenTarget.asActivityRecord() : null; - if (openActivity == null) { + final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface(); + final ActivityRecord[] openingActivities = getTopOpenActivities(mOpenTargets); + + if (shouldLaunchBehind && openingActivities == null) { Slog.e(TAG, "No opening activity"); return null; } - if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) { + if (!composeAnimations(mCloseTarget, mOpenTargets, openingActivities)) { return null; } mCloseTarget.mTransitionController.mSnapshotController .mActivitySnapshotController.clearOnBackPressedActivities(); - applyPreviewStrategy(mOpenAdaptor, openActivity); + applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor.mAdaptors, openingActivities); final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(); final RemoteAnimationTarget[] targets = getAnimationTargets(); @@ -1210,6 +1456,7 @@ class BackNavigationController { // animation was canceled return; } + mOpenAnimAdaptor.onAnimationFinish(); if (!triggerBack) { clearBackAnimateTarget(); } else { @@ -1223,6 +1470,41 @@ class BackNavigationController { } } + /** + * Finds next opening activity(ies) based on open targets, which could be: + * 1. If the open window is Task, then the open activity can either be an activity, or + * two activities inside two TaskFragments + * 2. If the open window is Activity, then the open window can be an activity, or two + * adjacent TaskFragments below it. + */ + @Nullable + private static ActivityRecord[] getTopOpenActivities( + @NonNull WindowContainer[] openWindows) { + ActivityRecord[] openActivities = null; + final WindowContainer mainTarget = openWindows[0]; + if (mainTarget.asTask() != null) { + final ArrayList<ActivityRecord> inTaskActivities = new ArrayList<>(); + final Task task = mainTarget.asTask(); + final ActivityRecord tmpPreActivity = task.getTopNonFinishingActivity(); + if (tmpPreActivity != null) { + inTaskActivities.add(tmpPreActivity); + findAdjacentActivityIfExist(tmpPreActivity, inTaskActivities); + } + + openActivities = new ActivityRecord[inTaskActivities.size()]; + for (int i = inTaskActivities.size() - 1; i >= 0; --i) { + openActivities[i] = inTaskActivities.get(i); + } + } else if (mainTarget.asActivityRecord() != null) { + final int size = openWindows.length; + openActivities = new ActivityRecord[size]; + for (int i = size - 1; i >= 0; --i) { + openActivities[i] = openWindows[i].asActivityRecord(); + } + } + return openActivities; + } + private static void setLaunchBehind(@NonNull ActivityRecord activity) { if (!activity.isVisibleRequested()) { activity.setVisibility(true); @@ -1311,7 +1593,7 @@ class BackNavigationController { static TaskSnapshot getSnapshot(@NonNull WindowContainer w) { if (w.asTask() != null) { final Task task = w.asTask(); - return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( + return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( task.mTaskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); } @@ -1340,8 +1622,10 @@ class BackNavigationController { proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress); proto.write(LAST_BACK_TYPE, mLastBackType); proto.write(SHOW_WALLPAPER, mShowWallpaper); - if (mAnimationHandler.mOpenActivity != null) { - mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY); + if (mAnimationHandler.mOpenAnimAdaptor != null + && mAnimationHandler.mOpenAnimAdaptor.mAdaptors.length > 0) { + mAnimationHandler.mOpenActivities[0].writeNameToProto( + proto, MAIN_OPEN_ACTIVITY); } else { proto.write(MAIN_OPEN_ACTIVITY, ""); } diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java index 022ef6152929..8717098ff8e8 100644 --- a/services/core/java/com/android/server/wm/ContentRecorder.java +++ b/services/core/java/com/android/server/wm/ContentRecorder.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Context.MEDIA_PROJECTION_SERVICE; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; @@ -100,6 +101,8 @@ final class ContentRecorder implements WindowContainerListener { @Configuration.Orientation private int mLastOrientation = ORIENTATION_UNDEFINED; + private int mLastWindowingMode = WINDOWING_MODE_UNDEFINED; + private final boolean mCorrectForAnisotropicPixels; ContentRecorder(@NonNull DisplayContent displayContent) { @@ -156,7 +159,8 @@ final class ContentRecorder implements WindowContainerListener { * Handle a configuration change on the display content, and resize recording if needed. * @param lastOrientation the prior orientation of the configuration */ - void onConfigurationChanged(@Configuration.Orientation int lastOrientation) { + void onConfigurationChanged( + @Configuration.Orientation int lastOrientation, int lastWindowingMode) { // Update surface for MediaProjection, if this DisplayContent is being used for recording. if (!isCurrentlyRecording() || mLastRecordedBounds == null) { return; @@ -185,6 +189,16 @@ final class ContentRecorder implements WindowContainerListener { } } + // Record updated windowing mode, if necessary. + int recordedContentWindowingMode = mRecordedWindowContainer.getWindowingMode(); + if (lastWindowingMode != recordedContentWindowingMode) { + mMediaProjectionManager.notifyWindowingModeChanged( + mContentRecordingSession.getContentToRecord(), + mContentRecordingSession.getTargetUid(), + recordedContentWindowingMode + ); + } + ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: Display %d was already recording, so apply " + "transformations if necessary", @@ -327,8 +341,10 @@ final class ContentRecorder implements WindowContainerListener { return; } + final int contentToRecord = mContentRecordingSession.getContentToRecord(); + // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are inaccurate. - if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) { + if (contentToRecord == RECORD_CONTENT_TASK) { if (mRecordedWindowContainer.asTask().inPinnedWindowingMode()) { ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: Display %d should start recording, but " @@ -375,7 +391,7 @@ final class ContentRecorder implements WindowContainerListener { // Notify the client about the visibility of the mirrored region, now that we have begun // capture. - if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) { + if (contentToRecord == RECORD_CONTENT_TASK) { mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged( mRecordedWindowContainer.asTask().isVisibleRequested()); } else { @@ -385,6 +401,11 @@ final class ContentRecorder implements WindowContainerListener { currentDisplayState != DISPLAY_STATE_OFF); } + // Record initial windowing mode after recording starts. + mMediaProjectionManager.notifyWindowingModeChanged( + contentToRecord, mContentRecordingSession.getTargetUid(), + mRecordedWindowContainer.getWindowConfiguration().getWindowingMode()); + // No need to clean up. In SurfaceFlinger, parents hold references to their children. The // mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is // holding a reference to it. Therefore, the mirrored SurfaceControl will be cleaned up @@ -617,8 +638,9 @@ final class ContentRecorder implements WindowContainerListener { Configuration mergedOverrideConfiguration) { WindowContainerListener.super.onMergedOverrideConfigurationChanged( mergedOverrideConfiguration); - onConfigurationChanged(mLastOrientation); + onConfigurationChanged(mLastOrientation, mLastWindowingMode); mLastOrientation = mergedOverrideConfiguration.orientation; + mLastWindowingMode = mergedOverrideConfiguration.windowConfiguration.getWindowingMode(); } // WindowContainerListener @@ -635,6 +657,7 @@ final class ContentRecorder implements WindowContainerListener { void stopActiveProjection(); void notifyActiveProjectionCapturedContentResized(int width, int height); void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible); + void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode); } private static final class RemoteMediaProjectionManagerWrapper implements @@ -700,6 +723,22 @@ final class ContentRecorder implements WindowContainerListener { } } + @Override + public void notifyWindowingModeChanged(int contentToRecord, int targetUid, + int windowingMode) { + fetchMediaProjectionManager(); + if (mIMediaProjectionManager == null) { + return; + } + try { + mIMediaProjectionManager.notifyWindowingModeChanged( + contentToRecord, targetUid, windowingMode); + } catch (RemoteException e) { + ProtoLog.e(WM_DEBUG_CONTENT_RECORDING, + "Content Recording: Unable to tell log windowing mode change: %s", e); + } + } + private void fetchMediaProjectionManager() { if (mIMediaProjectionManager != null) { return; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 576e8a48ab31..c716879a5097 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2757,6 +2757,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override public void onConfigurationChanged(Configuration newParentConfig) { final int lastOrientation = getConfiguration().orientation; + final int lastWindowingMode = getWindowingMode(); super.onConfigurationChanged(newParentConfig); if (mDisplayPolicy != null) { mDisplayPolicy.onConfigurationChanged(); @@ -2768,7 +2769,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Update surface for MediaProjection, if this DisplayContent is being used for recording. if (mContentRecorder != null) { - mContentRecorder.onConfigurationChanged(lastOrientation); + mContentRecorder.onConfigurationChanged(lastOrientation, lastWindowingMode); } if (lastOrientation != getConfiguration().orientation) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 4a467dfbcd14..b1c1fd6e9d8c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1681,8 +1681,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return false; } - if (!StorageManager.isUserKeyUnlocked(mCurrentUser)) { - // Can't launch home on secondary display areas if device is still locked. + if (!StorageManager.isCeStorageUnlocked(mCurrentUser)) { + // Can't launch home on secondary display areas if CE storage is still locked. return false; } diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 3775ccd79d4c..a756847d59ea 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -86,6 +86,7 @@ import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.protolog.common.ProtoLog; import com.android.server.LocalServices; import com.android.server.wm.WindowManagerService.H; +import com.android.window.flags.Flags; import java.io.PrintWriter; import java.util.Collections; @@ -166,8 +167,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { mCanSetUnrestrictedGestureExclusion = service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_GESTURE_EXCLUSION) == PERMISSION_GRANTED; - mCanAlwaysUpdateWallpaper = - service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER) + mCanAlwaysUpdateWallpaper = Flags.alwaysUpdateWallpaperPermission() + && service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER) == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; mDragDropController = mService.mDragDropController; diff --git a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java index c2e819e4c60b..4d4f99f5b68e 100644 --- a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java +++ b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.provider.DeviceConfig; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -98,27 +97,28 @@ final class SynchedDeviceConfig implements DeviceConfig.OnPropertiesChangedListe * @throws IllegalArgumentException {@code key} isn't recognised. */ boolean getFlagValue(@NonNull String key) { - return findEntry(key).map(SynchedDeviceConfigEntry::getValue) - .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key)); + final SynchedDeviceConfigEntry entry = mDeviceConfigEntries.get(key); + if (entry == null) { + throw new IllegalArgumentException("Unexpected flag name: " + key); + } + return entry.getValue(); } /** * @return {@code true} if the flag for the given {@code key} was enabled at build time. */ boolean isBuildTimeFlagEnabled(@NonNull String key) { - return findEntry(key).map(SynchedDeviceConfigEntry::isBuildTimeFlagEnabled) - .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key)); + final SynchedDeviceConfigEntry entry = mDeviceConfigEntries.get(key); + if (entry == null) { + throw new IllegalArgumentException("Unexpected flag name: " + key); + } + return entry.isBuildTimeFlagEnabled(); } private boolean isDeviceConfigFlagEnabled(@NonNull String key, boolean defaultValue) { return DeviceConfig.getBoolean(mNamespace, key, defaultValue); } - @NonNull - private Optional<SynchedDeviceConfigEntry> findEntry(@NonNull String key) { - return Optional.ofNullable(mDeviceConfigEntries.get(key)); - } - static class SynchedDeviceConfigBuilder { private final String mNamespace; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 6cad16c7db40..5c5a1e1d23dc 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -6541,11 +6541,11 @@ class Task extends TaskFragment { mActivityType = ACTIVITY_TYPE_STANDARD; } - if (mActivityType != ACTIVITY_TYPE_STANDARD + if (!DisplayContent.alwaysCreateRootTask(tda.getWindowingMode(), mActivityType) && mActivityType != ACTIVITY_TYPE_UNDEFINED) { - // For now there can be only one root task of a particular non-standard activity - // type on a display. So, get that ignoring whatever windowing mode it is - // currently in. + // Only Recents or Standard activity types are allowed to have more than one + // root task on a display, this is independent of whatever windowing mode it + // is currently in. Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType); if (rootTask != null) { throw new IllegalArgumentException("Root task=" + rootTask + " of activityType=" diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java index 34ae370a6099..e7a1cf106a44 100644 --- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java @@ -49,6 +49,7 @@ import android.view.RemoteAnimationDefinition; import android.view.WindowManager; import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizerController; +import android.window.RemoteTransition; import android.window.TaskFragmentInfo; import android.window.TaskFragmentOperation; import android.window.TaskFragmentParentInfo; @@ -566,7 +567,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr // Keep the calling identity to avoid unsecure change. synchronized (mGlobalLock) { if (isValidTransaction(wct)) { - applyTransaction(wct, transitionType, shouldApplyIndependently); + applyTransaction( + wct, transitionType, shouldApplyIndependently, null /* remoteTransition */); } // Even if the transaction is empty, we still need to invoke #onTransactionFinished // unless the organizer has been unregistered. @@ -587,14 +589,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr @Override public void applyTransaction(@NonNull WindowContainerTransaction wct, - @WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) { + @WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently, + @Nullable RemoteTransition remoteTransition) { // Keep the calling identity to avoid unsecure change. synchronized (mGlobalLock) { if (!isValidTransaction(wct)) { return; } mWindowOrganizerController.applyTaskFragmentTransactionLocked(wct, transitionType, - shouldApplyIndependently); + shouldApplyIndependently, remoteTransition); } } @@ -839,6 +842,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr Slog.e(TAG, "Caller organizer=" + organizer + " is no longer registered"); return false; } + return true; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 41e49b9f0466..12392a63a467 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -35,6 +35,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ParceledListSlice; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; @@ -733,7 +734,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { * the task was removed from hierarchy. */ int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root, - TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) { + TaskSnapshot taskSnapshot, Configuration configuration, + IWindowlessStartingSurfaceCallback callback) { final Task rootTask = task.getRootTask(); if (rootTask == null) { return INVALID_TASK_ID; @@ -743,6 +745,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return INVALID_TASK_ID; } final StartingWindowInfo info = task.getStartingWindowInfo(activity); + info.taskInfo.configuration.setTo(configuration); info.taskInfo.taskDescription = activity.taskDescription; info.taskSnapshot = taskSnapshot; info.windowlessStartingSurfaceCallback = callback; @@ -1195,8 +1198,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } public boolean handleInterceptBackPressedOnTaskRoot(Task task) { - if (task == null || !task.isOrganized() - || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) { + if (!shouldInterceptBackPressedOnRootTask(task)) { return false; } final TaskOrganizerPendingEventsQueue pendingEventsQueue = @@ -1229,6 +1231,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } + boolean shouldInterceptBackPressedOnRootTask(Task task) { + return task != null && task.isOrganized() + && mInterceptBackPressedOnRootTasks.contains(task.mTaskId); + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.print(prefix); pw.println("TaskOrganizerController:"); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9fb7e8ddbd20..9eb3389ff43c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2020,7 +2020,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (win.mActivityRecord != null) { - win.mActivityRecord.postWindowRemoveStartingWindowCleanup(); + win.mActivityRecord.postWindowRemoveStartingWindowCleanup(win); } if (win.mAttrs.type == TYPE_WALLPAPER) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index a8b9417edb9b..3497353a9849 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -103,6 +103,7 @@ import android.window.ITransitionMetricsReporter; import android.window.ITransitionPlayer; import android.window.IWindowContainerTransactionCallback; import android.window.IWindowOrganizerController; +import android.window.RemoteTransition; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; import android.window.TaskFragmentOperation; @@ -464,12 +465,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub * transition, which will be queued until the sync engine is * free if there is any other active sync. If {@code false}, * the {@code wct} will be directly applied to the active sync. + * @param remoteTransition {@link RemoteTransition} to apply for the transaction. Only available + * for system organizers. */ void applyTaskFragmentTransactionLocked(@NonNull WindowContainerTransaction wct, - @WindowManager.TransitionType int type, boolean shouldApplyIndependently) { + @WindowManager.TransitionType int type, boolean shouldApplyIndependently, + @Nullable RemoteTransition remoteTransition) { enforceTaskFragmentOrganizerPermission("applyTaskFragmentTransaction()", Objects.requireNonNull(wct.getTaskFragmentOrganizer()), Objects.requireNonNull(wct)); + if (remoteTransition != null && !mTaskFragmentOrganizerController.isSystemOrganizer( + wct.getTaskFragmentOrganizer().asBinder())) { + throw new SecurityException( + "Only a system organizer is allowed to use remote transition!"); + } final CallerInfo caller = new CallerInfo(); final long ident = Binder.clearCallingIdentity(); try { @@ -512,7 +521,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } mTransitionController.requestStartTransition(transition, null /* startTask */, - null /* remoteTransition */, null /* displayChange */); + remoteTransition, null /* displayChange */); transition.setAllReady(); }; mTransitionController.startCollectOrQueue(transition, doApply); diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index a4adf5866f3d..627461a2c6ed 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -862,37 +862,41 @@ public final class CredentialManagerService Slog.i(TAG, "isEnabledCredentialProviderService with componentName: " + componentName.flattenToString()); - // TODO(253157366): Check additional set of services. final int userId = UserHandle.getCallingUserId(); final int callingUid = Binder.getCallingUid(); enforceCallingPackage(callingPackage, callingUid); - synchronized (mLock) { - final List<CredentialManagerServiceImpl> services = - getServiceListForUserLocked(userId); - for (CredentialManagerServiceImpl s : services) { - final ComponentName serviceComponentName = s.getServiceComponentName(); - - if (serviceComponentName.equals(componentName)) { - if (!s.getServicePackageName().equals(callingPackage)) { - // The component name and the package name do not match. - MetricUtilities.logApiCalledSimpleV2( - ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, - ApiStatus.FAILURE, callingUid); - Slog.w( - TAG, - "isEnabledCredentialProviderService: Component name does " - + "not match package name."); - return false; - } - MetricUtilities.logApiCalledSimpleV2( - ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, - ApiStatus.SUCCESS, callingUid); - return true; - } - } + + if (componentName == null) { + Slog.w(TAG, "isEnabledCredentialProviderService componentName is null"); + // If the component name was not specified then throw an error and + // record a failure because the request failed due to invalid input. + MetricUtilities.logApiCalledSimpleV2( + ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, + ApiStatus.FAILURE, callingUid); + return false; + } + + if (!componentName.getPackageName().equals(callingPackage)) { + Slog.w(TAG, "isEnabledCredentialProviderService component name" + + " does not match requested component"); + // If the requested component name package name does not match + // the calling package then throw an error and record a failure + // metric (because the request failed due to invalid input). + MetricUtilities.logApiCalledSimpleV2( + ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, + ApiStatus.FAILURE, callingUid); + throw new IllegalArgumentException("provided component name does not match" + + " does not match requesting component"); } - return false; + final Set<ComponentName> enabledProviders = getEnabledProvidersForUser(userId); + MetricUtilities.logApiCalledSimpleV2( + ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE, + ApiStatus.SUCCESS, callingUid); + if (enabledProviders == null) { + return false; + } + return enabledProviders.contains(componentName); } @Override diff --git a/services/proguard.flags b/services/proguard.flags index e11e613adb5c..261bb7cacdc4 100644 --- a/services/proguard.flags +++ b/services/proguard.flags @@ -47,6 +47,11 @@ -keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; } -keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; } -keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.display.feature.DisplayManagerFlags { *; } +-keep,allowoptimization,allowaccessmodification class android.app.admin.flags.FeatureFlagsImpl { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; } +-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; } +-keep,allowaccessmodification class android.app.admin.flags.Flags { *; } # Referenced via CarServiceHelperService in car-frameworks-service (avoid removing) -keep public class com.android.server.utils.Slogf { *; } @@ -99,9 +104,6 @@ -keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService { <methods>; } --keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { - <methods>; -} -keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager { *** usbDeviceRemoved(...); *** usbDeviceAdded(...); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java index e7f1d16e1dfd..5c8a19c76887 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -182,6 +182,10 @@ public class PackageArchiverTest { any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt()); doReturn(mIcon).when(mArchiveManager).decodeIcon( any(ArchiveState.ArchiveActivityInfo.class)); + Resources mockResources = mock(Resources.class); + doReturn(mockResources) + .when(mContext) + .getResources(); } @Test diff --git a/services/tests/servicestests/res/xml/user_100_v9.xml b/services/tests/servicestests/res/xml/user_100_v9.xml new file mode 100644 index 000000000000..03c08ed40828 --- /dev/null +++ b/services/tests/servicestests/res/xml/user_100_v9.xml @@ -0,0 +1,20 @@ +<user id="100" + serialNumber="0" + flags="3091" + type="android.os.usertype.full.SYSTEM" + created="0" + lastLoggedIn="0" + lastLoggedInFingerprint="0" + profileBadge="0"> + <restrictions no_oem_unlock="true" /> + <device_policy_local_restrictions> + <restrictions_user user_id="0"> + <restrictions no_camera="true" /> + </restrictions_user> + <restrictions_user user_id="100"> + <restrictions no_camera="true" /> + <restrictions no_install_unknown_sources="true" /> + </restrictions_user> + </device_policy_local_restrictions> + <ignorePrepareStorageErrors>false</ignorePrepareStorageErrors> +</user>
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index b9e45bab7ab3..82efdd3ce40a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -535,6 +535,78 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test + public void testOnClientChange_magnificationTripleTapEnabled_requestConnection() { + when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); + + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationSingleFingerTripleTapEnabledLocked(true); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr).requestConnection(true); + } + + @SmallTest + @Test + public void testOnClientChange_magnificationTripleTapDisabled_requestDisconnection() { + when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); + + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + //userState.setMagnificationSingleFingerTripleTapEnabledLocked(false); + userState.setMagnificationSingleFingerTripleTapEnabledLocked(false); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr).requestConnection(false); + } + + @SmallTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) + public void testOnClientChange_magnificationTwoFingerTripleTapEnabled_requestConnection() { + when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); + + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationTwoFingerTripleTapEnabledLocked(true); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr).requestConnection(true); + } + + @SmallTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) + public void testOnClientChange_magnificationTwoFingerTripleTapDisabled_requestDisconnection() { + when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); + + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + //userState.setMagnificationSingleFingerTripleTapEnabledLocked(false); + userState.setMagnificationTwoFingerTripleTapEnabledLocked(false); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr).requestConnection(false); + } + + @SmallTest + @Test public void testOnClientChange_boundServiceCanControlMagnification_requestConnection() { when(mProxyManager.canRetrieveInteractiveWindowsLocked()).thenReturn(false); @@ -547,6 +619,64 @@ public class AccessibilityManagerServiceTest { verify(mMockWindowMagnificationMgr).requestConnection(true); } + @SmallTest + @Test + public void testOnClientChange_magnificationTripleTapDisabled_removeMagnificationButton() { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + userState.setMagnificationSingleFingerTripleTapEnabledLocked(false); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt()); + } + + @SmallTest + @Test + public void testOnClientChange_magnificationTripleTapEnabled_keepMagnificationButton() { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationSingleFingerTripleTapEnabledLocked(true); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt()); + } + + @SmallTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) + public void onClientChange_magnificationTwoFingerTripleTapDisabled_removeMagnificationButton() { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + userState.setMagnificationTwoFingerTripleTapEnabledLocked(false); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr, atLeastOnce()).removeMagnificationButton(anyInt()); + } + + @SmallTest + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE) + public void onClientChange_magnificationTwoFingerTripleTapEnabled_keepMagnificationButton() { + final AccessibilityUserState userState = mA11yms.mUserStates.get( + mA11yms.getCurrentUserIdLocked()); + userState.setMagnificationCapabilitiesLocked(ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + userState.setMagnificationTwoFingerTripleTapEnabledLocked(true); + + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false); + + verify(mMockWindowMagnificationMgr, never()).removeMagnificationButton(anyInt()); + } + @Test public void testUnbindIme_whenServiceUnbinds() { setupAccessibilityServiceConnection(AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR); diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index ece3dfeabafa..097cc5177a83 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -17,12 +17,17 @@ package com.android.server.media.projection; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; import static android.media.projection.MediaProjectionManager.TYPE_MIRRORING; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY; import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK; import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN; +import static android.view.ContentRecordingSession.TARGET_UID_FULL_SCREEN; +import static android.view.ContentRecordingSession.TARGET_UID_UNKNOWN; +import static android.view.ContentRecordingSession.createDisplaySession; +import static android.view.ContentRecordingSession.createTaskSession; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -62,6 +67,7 @@ import android.os.UserHandle; import android.os.test.TestLooper; import android.platform.test.annotations.Presubmit; import android.view.ContentRecordingSession; +import android.view.ContentRecordingSession.RecordContent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.FlakyTest; @@ -99,7 +105,7 @@ public class MediaProjectionManagerServiceTest { private final ApplicationInfo mAppInfo = new ApplicationInfo(); private final TestLooper mTestLooper = new TestLooper(); private static final ContentRecordingSession DISPLAY_SESSION = - ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY); + createDisplaySession(DEFAULT_DISPLAY); // Callback registered by an app on a MediaProjection instance. private final FakeIMediaProjectionCallback mIMediaProjectionCallback = new FakeIMediaProjectionCallback(); @@ -142,7 +148,7 @@ public class MediaProjectionManagerServiceTest { private MediaProjectionManagerService mService; private OffsettableClock mClock; private ContentRecordingSession mWaitingDisplaySession = - ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY); + createDisplaySession(DEFAULT_DISPLAY); @Mock private ActivityManagerInternal mAmInternal; @@ -333,7 +339,7 @@ public class MediaProjectionManagerServiceTest { projection.stop(); verify(mMediaProjectionMetricsLogger) - .logStopped(UID, ContentRecordingSession.TARGET_UID_UNKNOWN); + .logStopped(UID, TARGET_UID_UNKNOWN); } @Test @@ -351,7 +357,7 @@ public class MediaProjectionManagerServiceTest { projection.stop(); verify(mMediaProjectionMetricsLogger) - .logStopped(UID, ContentRecordingSession.TARGET_UID_FULL_SCREEN); + .logStopped(UID, TARGET_UID_FULL_SCREEN); } @Test @@ -366,7 +372,7 @@ public class MediaProjectionManagerServiceTest { .when(mWindowManagerInternal) .setContentRecordingSession(any(ContentRecordingSession.class)); ContentRecordingSession taskSession = - ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid); + createTaskSession(mock(IBinder.class), targetUid); service.setContentRecordingSession(taskSession); projection.stop(); @@ -695,6 +701,26 @@ public class MediaProjectionManagerServiceTest { verify(mMediaProjectionMetricsLogger).logAppSelectorDisplayed(hostUid); } + @Test + public void notifyWindowingModeChanged_forwardsToLogger() throws Exception { + int targetUid = 123; + mService = + new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector); + + ContentRecordingSession taskSession = + createTaskSession(mock(IBinder.class), targetUid); + mService.setContentRecordingSession(taskSession); + + MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); + projection.start(mIMediaProjectionCallback); + + mService.notifyWindowingModeChanged( + RECORD_CONTENT_TASK, targetUid, WINDOWING_MODE_MULTI_WINDOW); + + verify(mMediaProjectionMetricsLogger).logChangedWindowingMode(RECORD_CONTENT_TASK, + projection.uid, targetUid, WINDOWING_MODE_MULTI_WINDOW); + } + /** * Executes and validates scenario where the consent result indicates the projection ends. */ @@ -755,7 +781,7 @@ public class MediaProjectionManagerServiceTest { */ private void testSetUserReviewGrantedConsentResult_startedSession( @ReviewGrantedConsentResult int consentResult, - @ContentRecordingSession.RecordContent int recordedContent) + @RecordContent int recordedContent) throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); projection.setLaunchCookie(mock(IBinder.class)); @@ -777,7 +803,7 @@ public class MediaProjectionManagerServiceTest { */ private void testSetUserReviewGrantedConsentResult_failedToStartSession( @ReviewGrantedConsentResult int consentResult, - @ContentRecordingSession.RecordContent int recordedContent) + @RecordContent int recordedContent) throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); projection.start(mIMediaProjectionCallback); @@ -889,7 +915,7 @@ public class MediaProjectionManagerServiceTest { int targetUid = 123455; ContentRecordingSession taskSession = - ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid); + createTaskSession(mock(IBinder.class), targetUid); service.setContentRecordingSession(taskSession); verify(mMediaProjectionMetricsLogger).logInProgress(projection.uid, targetUid); @@ -970,7 +996,7 @@ public class MediaProjectionManagerServiceTest { verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any()); } - private void verifySetSessionWithContent(@ContentRecordingSession.RecordContent int content) { + private void verifySetSessionWithContent(@RecordContent int content) { verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession( mSessionCaptor.capture()); assertThat(mSessionCaptor.getValue()).isNotNull(); diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java index ad1cd6eca5ac..72ce9fe9a23f 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java @@ -16,6 +16,14 @@ package com.android.server.media.projection; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +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.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; +import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; + import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED; @@ -24,6 +32,13 @@ import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED; import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN; +import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -38,10 +53,14 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.FrameworkStatsLog; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.time.Duration; @@ -60,12 +79,18 @@ public class MediaProjectionMetricsLoggerTest { private static final int TEST_TARGET_UID = 456; private static final int TEST_CREATION_SOURCE = 789; + private static final int TEST_WINDOWING_MODE = 987; + private static final int TEST_CONTENT_TO_RECORD = 654; + @Mock private FrameworkStatsLogWrapper mFrameworkStatsLogWrapper; @Mock private MediaProjectionSessionIdGenerator mSessionIdGenerator; @Mock private MediaProjectionTimestampStore mTimestampStore; private MediaProjectionMetricsLogger mLogger; + @Rule + public Expect mExpect = Expect.create(); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -93,7 +118,7 @@ public class MediaProjectionMetricsLoggerTest { public void logInitiated_logsHostUid() { mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE); - verifyHostUidLogged(TEST_HOST_UID); + verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test @@ -107,7 +132,7 @@ public class MediaProjectionMetricsLoggerTest { public void logInitiated_logsUnknownTargetUid() { mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE); - verifyTargetUidLogged(-2); + verifyStageChangedTargetUidLogged(-2); } @Test @@ -178,14 +203,14 @@ public class MediaProjectionMetricsLoggerTest { public void logStopped_logsHostUid() { mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); - verifyHostUidLogged(TEST_HOST_UID); + verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test public void logStopped_logsTargetUid() { mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID); - verifyTargetUidLogged(TEST_TARGET_UID); + verifyStageChangedTargetUidLogged(TEST_TARGET_UID); } @Test @@ -263,14 +288,14 @@ public class MediaProjectionMetricsLoggerTest { public void logInProgress_logsHostUid() { mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID); - verifyHostUidLogged(TEST_HOST_UID); + verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test public void logInProgress_logsTargetUid() { mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID); - verifyTargetUidLogged(TEST_TARGET_UID); + verifyStageChangedTargetUidLogged(TEST_TARGET_UID); } @Test @@ -336,14 +361,14 @@ public class MediaProjectionMetricsLoggerTest { public void logPermissionRequestDisplayed_logsHostUid() { mLogger.logPermissionRequestDisplayed(TEST_HOST_UID); - verifyHostUidLogged(TEST_HOST_UID); + verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test public void logPermissionRequestDisplayed_logsUnknownTargetUid() { mLogger.logPermissionRequestDisplayed(TEST_HOST_UID); - verifyTargetUidLogged(-2); + verifyStageChangedTargetUidLogged(-2); } @Test @@ -409,14 +434,14 @@ public class MediaProjectionMetricsLoggerTest { public void logAppSelectorDisplayed_logsHostUid() { mLogger.logAppSelectorDisplayed(TEST_HOST_UID); - verifyHostUidLogged(TEST_HOST_UID); + verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test public void logAppSelectorDisplayed_logsUnknownTargetUid() { mLogger.logAppSelectorDisplayed(TEST_HOST_UID); - verifyTargetUidLogged(-2); + verifyStageChangedTargetUidLogged(-2); } @Test @@ -492,14 +517,14 @@ public class MediaProjectionMetricsLoggerTest { public void logProjectionPermissionRequestCancelled_logsHostUid() { mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID); - verifyHostUidLogged(TEST_HOST_UID); + verifyStateChangedHostUidLogged(TEST_HOST_UID); } @Test public void logProjectionPermissionRequestCancelled_logsUnknownTargetUid() { mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID); - verifyTargetUidLogged(-2); + verifyStageChangedTargetUidLogged(-2); } @Test @@ -510,9 +535,88 @@ public class MediaProjectionMetricsLoggerTest { MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN); } + @Test + public void logWindowingModeChanged_logsTargetChangedAtomId() { + mLogger.logChangedWindowingMode( + TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE); + + verifyTargetChangedAtomIdLogged(); + } + + @Test + public void logWindowingModeChanged_logsTargetType() { + MediaProjectionMetricsLogger logger = Mockito.spy(mLogger); + final int testTargetType = 111; + when(logger.contentToRecordToTargetType(TEST_CONTENT_TO_RECORD)).thenReturn(testTargetType); + logger.logChangedWindowingMode( + TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE); + verifyTargetTypeLogged(testTargetType); + } + + @Test + public void logWindowingModeChanged_logsHostUid() { + mLogger.logChangedWindowingMode( + TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE); + verifyTargetChangedHostUidLogged(TEST_HOST_UID); + } + + @Test + public void logWindowingModeChanged_logsTargetUid() { + mLogger.logChangedWindowingMode( + TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE); + verifyTargetChangedTargetUidLogged(TEST_TARGET_UID); + } + + @Test + public void logWindowingModeChanged_logsTargetWindowingMode() { + MediaProjectionMetricsLogger logger = Mockito.spy(mLogger); + final int testTargetWindowingMode = 222; + when(logger.windowingModeToTargetWindowingMode(TEST_WINDOWING_MODE)) + .thenReturn(testTargetWindowingMode); + logger.logChangedWindowingMode( + TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE); + verifyWindowingModeLogged(testTargetWindowingMode); + } + + @Test + public void testContentToRecordToTargetType() { + mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_DISPLAY)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY); + + mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_TASK)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK); + + mExpect.that(mLogger.contentToRecordToTargetType(2)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN); + + mExpect.that(mLogger.contentToRecordToTargetType(-1)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN); + + mExpect.that(mLogger.contentToRecordToTargetType(100)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN); + } + + @Test + public void testWindowingModeToTargetWindowingMode() { + mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_FULLSCREEN)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN); + + mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_MULTI_WINDOW)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN); + + mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_FREEFORM)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM); + + mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_PINNED)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN); + + mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_UNDEFINED)) + .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN); + } + private void verifyStateChangedAtomIdLogged() { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ eq(FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED), /* sessionId= */ anyInt(), /* state= */ anyInt(), @@ -525,7 +629,7 @@ public class MediaProjectionMetricsLoggerTest { private void verifyStateLogged(int state) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ anyInt(), eq(state), @@ -536,9 +640,9 @@ public class MediaProjectionMetricsLoggerTest { /* creationSource= */ anyInt()); } - private void verifyHostUidLogged(int hostUid) { + private void verifyStateChangedHostUidLogged(int hostUid) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ anyInt(), /* state= */ anyInt(), @@ -551,7 +655,7 @@ public class MediaProjectionMetricsLoggerTest { private void verifyCreationSourceLogged(int creationSource) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ anyInt(), /* state= */ anyInt(), @@ -562,9 +666,9 @@ public class MediaProjectionMetricsLoggerTest { eq(creationSource)); } - private void verifyTargetUidLogged(int targetUid) { + private void verifyStageChangedTargetUidLogged(int targetUid) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ anyInt(), /* state= */ anyInt(), @@ -577,7 +681,7 @@ public class MediaProjectionMetricsLoggerTest { private void verifyTimeSinceLastActiveSessionLogged(int timeSinceLastActiveSession) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ anyInt(), /* state= */ anyInt(), @@ -590,7 +694,7 @@ public class MediaProjectionMetricsLoggerTest { private void verifySessionIdLogged(int newSessionId) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ eq(newSessionId), /* state= */ anyInt(), @@ -603,7 +707,7 @@ public class MediaProjectionMetricsLoggerTest { private void verifyPreviousStateLogged(int previousState) { verify(mFrameworkStatsLogWrapper) - .write( + .writeStateChanged( /* code= */ anyInt(), /* sessionId= */ anyInt(), /* state= */ anyInt(), @@ -613,4 +717,59 @@ public class MediaProjectionMetricsLoggerTest { /* timeSinceLastActive= */ anyInt(), /* creationSource= */ anyInt()); } + + private void verifyTargetChangedAtomIdLogged() { + verify(mFrameworkStatsLogWrapper) + .writeTargetChanged( + eq(FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED), + /* sessionId= */ anyInt(), + /* targetType= */ anyInt(), + /* hostUid= */ anyInt(), + /* targetUid= */ anyInt(), + /* targetWindowingMode= */ anyInt()); + } + + private void verifyTargetTypeLogged(int targetType) { + verify(mFrameworkStatsLogWrapper) + .writeTargetChanged( + /* code= */ anyInt(), + /* sessionId= */ anyInt(), + eq(targetType), + /* hostUid= */ anyInt(), + /* targetUid= */ anyInt(), + /* targetWindowingMode= */ anyInt()); + } + + private void verifyTargetChangedHostUidLogged(int hostUid) { + verify(mFrameworkStatsLogWrapper) + .writeTargetChanged( + /* code= */ anyInt(), + /* sessionId= */ anyInt(), + /* targetType= */ anyInt(), + eq(hostUid), + /* targetUid= */ anyInt(), + /* targetWindowingMode= */ anyInt()); + } + + private void verifyTargetChangedTargetUidLogged(int targetUid) { + verify(mFrameworkStatsLogWrapper) + .writeTargetChanged( + /* code= */ anyInt(), + /* sessionId= */ anyInt(), + /* targetType= */ anyInt(), + /* hostUid= */ anyInt(), + eq(targetUid), + /* targetWindowingMode= */ anyInt()); + } + + private void verifyWindowingModeLogged(int targetWindowingMode) { + verify(mFrameworkStatsLogWrapper) + .writeTargetChanged( + /* code= */ anyInt(), + /* sessionId= */ anyInt(), + /* targetType= */ anyInt(), + /* hostUid= */ anyInt(), + /* targetUid= */ anyInt(), + eq(targetWindowingMode)); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 253592c9a07d..d1b2e8e6d868 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -43,6 +43,7 @@ import android.annotation.UserIdInt; import android.app.PropertyInvalidatedCache; import android.content.pm.UserInfo; import android.content.pm.UserInfo.UserInfoFlag; +import android.content.res.Resources; import android.multiuser.Flags; import android.os.Looper; import android.os.Parcel; @@ -50,21 +51,26 @@ import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; +import android.util.Xml; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; +import com.android.frameworks.servicestests.R; import com.android.server.LocalServices; import com.android.server.pm.UserManagerService.UserData; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -77,6 +83,7 @@ import java.util.List; @MediumTest public class UserManagerServiceUserInfoTest { private UserManagerService mUserManagerService; + private Resources mResources; @Before public void setup() { @@ -96,6 +103,8 @@ public class UserManagerServiceUserInfoTest { assertEquals("Multiple users so this test can't run.", 1, users.size()); assertEquals("Only user present isn't the system user.", UserHandle.USER_SYSTEM, users.get(0).id); + + mResources = InstrumentationRegistry.getTargetContext().getResources(); } @Test @@ -109,7 +118,7 @@ public class UserManagerServiceUserInfoTest { byte[] bytes = baos.toByteArray(); UserData read = mUserManagerService.readUserLP( - data.info.id, new ByteArrayInputStream(bytes)); + data.info.id, new ByteArrayInputStream(bytes), 0); assertUserInfoEquals(data.info, read.info, /* parcelCopy= */ false); } @@ -146,11 +155,13 @@ public class UserManagerServiceUserInfoTest { // Clear the restrictions to see if they are properly read in from the user file. setUserRestrictions(data.info.id, globalRestriction, localRestriction, false); + final int userVersion = 10; //read the secondary and SYSTEM user file to fetch local/global device policy restrictions. - mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes)); + mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(secondaryUserBytes), + userVersion); if (Flags.saveGlobalAndGuestRestrictionsOnSystemUserXmlReadOnly()) { mUserManagerService.readUserLP(UserHandle.USER_SYSTEM, - new ByteArrayInputStream(systemUserBytes)); + new ByteArrayInputStream(systemUserBytes), userVersion); } assertTrue(mUserManagerService.hasUserRestrictionOnAnyUser(globalRestriction)); @@ -303,6 +314,45 @@ public class UserManagerServiceUserInfoTest { assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO)); } + /** Tests readUserLP upgrading from version 9 to 10+. */ + @Test + public void testUserRestrictionsUpgradeFromV9() throws Exception { + final String[] localRestrictions = new String[] { + UserManager.DISALLOW_CAMERA, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + }; + + final int userId = 100; + UserData data = new UserData(); + data.info = createUser(userId, FLAG_FULL, "A type"); + + mUserManagerService.putUserInfo(data.info); + + for (String restriction : localRestrictions) { + assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId)); + assertFalse(mUserManagerService.hasUserRestriction(restriction, userId)); + } + + // Convert the xml resource to the system storage xml format. + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream os = new DataOutputStream(baos); + XmlPullParser in = mResources.getXml(R.xml.user_100_v9); + XmlSerializer out = Xml.newBinarySerializer(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + Xml.copy(in, out); + byte[] userBytes = baos.toByteArray(); + baos.reset(); + + final int userVersion = 9; + mUserManagerService.readUserLP(data.info.id, new ByteArrayInputStream(userBytes), + userVersion); + + for (String restriction : localRestrictions) { + assertFalse(mUserManagerService.hasBaseUserRestriction(restriction, userId)); + assertTrue(mUserManagerService.hasUserRestriction(restriction, userId)); + } + } + /** Creates a UserInfo with the given flags and userType. */ private UserInfo createUser(@UserIdInt int userId, @UserInfoFlag int flags, String userType) { return new UserInfo(userId, "A Name", "A path", flags, userType); diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index dd7dec0bdb2b..7b1fa036a016 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -76,6 +76,7 @@ import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; +import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -601,30 +602,33 @@ public class BackNavigationControllerTests extends WindowTestsBase { final Task task = createTask(mDefaultDisplay); final ActivityRecord bottomActivity = createActivityRecord(task); final ActivityRecord homeActivity = mRootHomeTask.getTopNonFinishingActivity(); - + final ArrayList<ActivityRecord> openActivities = new ArrayList<>(); + openActivities.add(homeActivity); final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toHomeBuilder = animationHandler.prepareAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME, - mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity); + mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, openActivities); assertTrue(toHomeBuilder.mIsLaunchBehind); toHomeBuilder.build(); - verify(mAtm.mTaskOrganizerController, never()) - .addWindowlessStartingSurface(any(), any(), any(), any(), any()); + verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface( + any(), any(), any(), any(), any(), any()); animationHandler.clearBackAnimateTarget(); + openActivities.clear(); // Back to ACTIVITY and TASK have the same logic, just with different target. final ActivityRecord topActivity = createActivityRecord(task); + openActivities.add(bottomActivity); final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toActivityBuilder = animationHandler.prepareAnimation( BackNavigationInfo.TYPE_CROSS_ACTIVITY, mBackAnimationAdapter, task, task, - topActivity, bottomActivity); + topActivity, openActivities); assertFalse(toActivityBuilder.mIsLaunchBehind); toActivityBuilder.build(); if (preferWindowlessSurface) { - verify(mAtm.mTaskOrganizerController) - .addWindowlessStartingSurface(any(), any(), any(), any(), any()); + verify(mAtm.mTaskOrganizerController).addWindowlessStartingSurface( + any(), any(), any(), any(), any(), any()); } else { - verify(mAtm.mTaskOrganizerController, never()) - .addWindowlessStartingSurface(any(), any(), any(), any(), any()); + verify(mAtm.mTaskOrganizerController, never()).addWindowlessStartingSurface( + any(), any(), any(), any(), any(), any()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java index 78566fb06179..887e5ee0c58a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java @@ -42,6 +42,7 @@ import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import android.app.WindowConfiguration; import android.content.pm.ActivityInfo; @@ -232,7 +233,7 @@ public class ContentRecorderTests extends WindowTestsBase { @Test public void testOnConfigurationChanged_neverRecording() { defaultInit(); - mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT); + mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN); verify(mTransaction, never()).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat()); verify(mTransaction, never()).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(), @@ -248,7 +249,7 @@ public class ContentRecorderTests extends WindowTestsBase { @Configuration.Orientation final int lastOrientation = mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; - mContentRecorder.onConfigurationChanged(lastOrientation); + mContentRecorder.onConfigurationChanged(lastOrientation, WINDOWING_MODE_FULLSCREEN); verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat()); @@ -266,7 +267,8 @@ public class ContentRecorderTests extends WindowTestsBase { // The user rotates the device, so the host app resizes the virtual display for the capture. resizeDisplay(mDisplayContent, newWidth, mSurfaceSize.y); resizeDisplay(mVirtualDisplayContent, newWidth, mSurfaceSize.y); - mContentRecorder.onConfigurationChanged(mDisplayContent.getConfiguration().orientation); + mContentRecorder.onConfigurationChanged( + mDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN); verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat()); @@ -283,7 +285,7 @@ public class ContentRecorderTests extends WindowTestsBase { // Change a value that we shouldn't rely upon; it has the wrong type. mVirtualDisplayContent.setOverrideOrientation(SCREEN_ORIENTATION_FULL_SENSOR); mContentRecorder.onConfigurationChanged( - mVirtualDisplayContent.getConfiguration().orientation); + mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN); // No resize is issued, only the initial transformations when we started recording. verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(), @@ -307,7 +309,7 @@ public class ContentRecorderTests extends WindowTestsBase { doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize( anyInt()); mContentRecorder.onConfigurationChanged( - mVirtualDisplayContent.getConfiguration().orientation); + mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN); // No resize is issued, only the initial transformations when we started recording. verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(), @@ -379,6 +381,55 @@ public class ContentRecorderTests extends WindowTestsBase { } @Test + public void testTaskWindowingModeChanged_changeWindowMode_notifyWindowModeChanged() { + defaultInit(); + // WHEN a recording is ongoing. + mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + mContentRecorder.setContentRecordingSession(mTaskSession); + mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); + + // THEN the windowing mode change callback is notified. + verify(mMediaProjectionManagerWrapper) + .notifyWindowingModeChanged(mTaskSession.getContentToRecord(), + mTaskSession.getTargetUid(), WINDOWING_MODE_FULLSCREEN); + + // WHEN a configuration change arrives, and the task is now multi-window mode. + mTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + Configuration configuration = mTask.getConfiguration(); + mTask.onConfigurationChanged(configuration); + + // THEN windowing mode change callback is notified again. + verify(mMediaProjectionManagerWrapper) + .notifyWindowingModeChanged(mTaskSession.getContentToRecord(), + mTaskSession.getTargetUid(), WINDOWING_MODE_MULTI_WINDOW); + } + + @Test + public void testTaskWindowingModeChanged_sameWindowMode_notifyWindowModeChanged() { + defaultInit(); + // WHEN a recording is ongoing. + mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + mContentRecorder.setContentRecordingSession(mTaskSession); + mContentRecorder.updateRecording(); + assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); + + // THEN the windowing mode change callback is notified. + verify(mMediaProjectionManagerWrapper) + .notifyWindowingModeChanged(mTaskSession.getContentToRecord(), + mTaskSession.getTargetUid(), WINDOWING_MODE_FULLSCREEN); + + // WHEN a configuration change arrives, and the task is STILL fullscreen. + mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + Configuration configuration = mTask.getConfiguration(); + mTask.onConfigurationChanged(configuration); + + // THEN the windowing mode change callback is NOT called notified again. + verify(mMediaProjectionManagerWrapper, times(1)) + .notifyWindowingModeChanged(anyInt(), anyInt(), anyInt()); + } + + @Test public void testTaskWindowingModeChanged_pip_stopsRecording() { defaultInit(); // WHEN a recording is ongoing. @@ -421,9 +472,12 @@ public class ContentRecorderTests extends WindowTestsBase { mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); - // THEN the visibility change callback is notified. + // THEN the visibility change & windowing mode change callbacks are notified. verify(mMediaProjectionManagerWrapper) .notifyActiveProjectionCapturedContentVisibilityChanged(true); + verify(mMediaProjectionManagerWrapper) + .notifyWindowingModeChanged(mTaskSession.getContentToRecord(), + mTaskSession.getTargetUid(), mRootWindowContainer.getWindowingMode()); } @Test @@ -434,9 +488,12 @@ public class ContentRecorderTests extends WindowTestsBase { mContentRecorder.updateRecording(); assertThat(mContentRecorder.isCurrentlyRecording()).isTrue(); - // THEN the visibility change callback is notified. + // THEN the visibility change & windowing mode change callbacks are notified. verify(mMediaProjectionManagerWrapper) .notifyActiveProjectionCapturedContentVisibilityChanged(true); + verify(mMediaProjectionManagerWrapper) + .notifyWindowingModeChanged(mDisplaySession.getContentToRecord(), + mDisplaySession.getTargetUid(), mRootWindowContainer.getWindowingMode()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index c241033c69d3..eb78906f570d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -872,7 +872,7 @@ public class RootWindowContainerTests extends WindowTestsBase { new TestDisplayContent.Builder(mAtm, 1000, 1500) .setSystemDecorations(true).build(); - // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false. + // Use invalid user id to let StorageManager.isCeStorageUnlocked() return false. final int currentUser = mRootWindowContainer.mCurrentUser; mRootWindowContainer.mCurrentUser = -1; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index c57b05130e77..8a90f127f4eb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -86,7 +86,9 @@ import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.RemoteAnimationDefinition; import android.view.SurfaceControl; +import android.window.IRemoteTransition; import android.window.ITaskFragmentOrganizer; +import android.window.RemoteTransition; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; import android.window.TaskFragmentInfo; @@ -546,6 +548,35 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testApplyTransaction_disallowRemoteTransitionForNonSystemOrganizer() { + mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); + mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, + "Test:TaskFragmentOrganizer" /* processName */); + + // Throw exception if the transaction has remote transition and is not requested by system + // organizer + assertThrows(SecurityException.class, () -> + mController.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, + true /* shouldApplyIndependently */, + new RemoteTransition(mock(IRemoteTransition.class)))); + } + + @Test + public void testApplyTransaction_allowRemoteTransitionForSystemOrganizer() { + mController.unregisterOrganizer(mIOrganizer); + mController.registerOrganizerInternal(mIOrganizer, true /* isSystemOrganizer */); + + mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); + mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, + "Test:TaskFragmentOrganizer" /* processName */); + + // Remote transition is allowed for system organizer + mController.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, + true /* shouldApplyIndependently */, + new RemoteTransition(mock(IRemoteTransition.class))); + } + + @Test public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment() { // Throw exception if the transaction is trying to change a window that is not organized by // the organizer. @@ -1801,13 +1832,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { private void assertApplyTransactionDisallowed(WindowContainerTransaction t) { assertThrows(SecurityException.class, () -> mController.applyTransaction(t, TASK_FRAGMENT_TRANSIT_CHANGE, - false /* shouldApplyIndependently */)); + false /* shouldApplyIndependently */, null /* remoteTransition */)); } /** Asserts that applying the given transaction will not throw any exception. */ private void assertApplyTransactionAllowed(WindowContainerTransaction t) { mController.applyTransaction(t, TASK_FRAGMENT_TRANSIT_CHANGE, - false /* shouldApplyIndependently */); + false /* shouldApplyIndependently */, null /* remoteTransition */); } /** Asserts that there will be a transaction for TaskFragment appeared. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 699580a6536d..2c3917387aec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -614,7 +614,7 @@ public class WindowOrganizerTests extends WindowTestsBase { t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), true); mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked( t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE, - false /* shouldApplyIndependently */); + false /* shouldApplyIndependently */, null /* remoteTransition */); // Should be not visible and not focusable after the transaction. assertFalse(taskFragment.shouldBeVisible(null)); @@ -628,7 +628,7 @@ public class WindowOrganizerTests extends WindowTestsBase { t.setForceTranslucent(taskFragment.mRemoteToken.toWindowContainerToken(), false); mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked( t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE, - false /* shouldApplyIndependently */); + false /* shouldApplyIndependently */, null /* remoteTransition */); // Should be visible and focusable after the transaction. assertTrue(taskFragment.shouldBeVisible(null)); @@ -680,7 +680,7 @@ public class WindowOrganizerTests extends WindowTestsBase { assertThrows(SecurityException.class, () -> mWm.mAtmService.mWindowOrganizerController.applyTaskFragmentTransactionLocked( t, TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE, - false /* shouldApplyIndependently */) + false /* shouldApplyIndependently */, null /* remoteTransition */) ); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 1f32c978fad1..55fecfccf108 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -19,6 +19,7 @@ import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -54,6 +55,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.telecom.ClientTransactionalServiceRepository; import com.android.internal.telecom.ClientTransactionalServiceWrapper; import com.android.internal.telecom.ITelecomService; +import com.android.server.telecom.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -412,6 +414,14 @@ public class TelecomManager { "android.telecom.extra.CALL_CREATED_TIME_MILLIS"; /** + * The extra for call log uri that was used to mark missed calls as read when dialer gets the + * notification on reboot. + */ + @FlaggedApi(Flags.FLAG_ADD_CALL_URI_FOR_MISSED_CALLS) + public static final String EXTRA_CALL_LOG_URI = + "android.telecom.extra.CALL_LOG_URI"; + + /** * Optional extra for incoming containing a long which specifies the time the * call was answered by user. This value is in milliseconds. * @hide @@ -2361,6 +2371,11 @@ public class TelecomManager { * <p> * <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after * the call is placed in order for the notification to be non-dismissible. + * <p><b>Note</b>: Call Forwarding MMI codes can only be dialed by applications that are + * configured as the user defined default dialer or system dialer role. If a call containing a + * call forwarding MMI code is placed by an application that is not in one of these roles, the + * dialer will be launched with a UI showing the MMI code already populated so that the user can + * confirm the action before the call is placed. * @param address The address to make the call to. * @param extras Bundle of extras to use with the call. */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 042b2a3de0a1..4250bd14da52 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9692,6 +9692,7 @@ public class CarrierConfigManager { * * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT + * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED */ public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG = diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index c0d6b301def0..e9ea5a7c2ce2 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -17483,9 +17483,8 @@ public class TelephonyManager { * {@link CarrierConfigManager * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG} * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SLICING_ADDITIONAL_ERROR_CODES) public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 16; /** diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index 11cbcb1c149d..cb7926ca0608 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -568,6 +568,7 @@ public class ApnSetting implements Parcelable { private final int mSkip464Xlat; private final boolean mAlwaysOn; private final @InfrastructureBitmask int mInfrastructureBitmask; + private final boolean mEsimBootstrapProvisioning; /** * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought @@ -979,6 +980,18 @@ public class ApnSetting implements Parcelable { return mInfrastructureBitmask; } + /** + * Returns esim bootstrap provisioning flag for which the APN can be used on. For example, + * some APNs are only allowed to bring up network, when the device esim bootstrap provisioning + * is being activated. + * + * {@code true} if the APN is used for eSIM bootstrap provisioning, {@code false} otherwise. + * @hide + */ + public boolean isEsimBootstrapProvisioning() { + return mEsimBootstrapProvisioning; + } + private ApnSetting(Builder builder) { this.mEntryName = builder.mEntryName; this.mApnName = builder.mApnName; @@ -1016,6 +1029,7 @@ public class ApnSetting implements Parcelable { this.mSkip464Xlat = builder.mSkip464Xlat; this.mAlwaysOn = builder.mAlwaysOn; this.mInfrastructureBitmask = builder.mInfrastructureBitmask; + this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning; } /** @@ -1097,6 +1111,8 @@ public class ApnSetting implements Parcelable { .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1) .setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.Carriers.INFRASTRUCTURE_BITMASK))) + .setEsimBootstrapProvisioning(cursor.getInt( + cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1) .buildWithoutCheck(); } @@ -1137,6 +1153,7 @@ public class ApnSetting implements Parcelable { .setSkip464Xlat(apn.mSkip464Xlat) .setAlwaysOn(apn.mAlwaysOn) .setInfrastructureBitmask(apn.mInfrastructureBitmask) + .setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning) .buildWithoutCheck(); } @@ -1184,6 +1201,7 @@ public class ApnSetting implements Parcelable { sb.append(", ").append(mAlwaysOn); sb.append(", ").append(mInfrastructureBitmask); sb.append(", ").append(Objects.hash(mUser, mPassword)); + sb.append(", ").append(mEsimBootstrapProvisioning); return sb.toString(); } @@ -1247,7 +1265,7 @@ public class ApnSetting implements Parcelable { mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask, mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat, - mAlwaysOn, mInfrastructureBitmask); + mAlwaysOn, mInfrastructureBitmask, mEsimBootstrapProvisioning); } @Override @@ -1289,7 +1307,8 @@ public class ApnSetting implements Parcelable { && mCarrierId == other.mCarrierId && mSkip464Xlat == other.mSkip464Xlat && mAlwaysOn == other.mAlwaysOn - && mInfrastructureBitmask == other.mInfrastructureBitmask; + && mInfrastructureBitmask == other.mInfrastructureBitmask + && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning); } /** @@ -1340,7 +1359,8 @@ public class ApnSetting implements Parcelable { && Objects.equals(mCarrierId, other.mCarrierId) && Objects.equals(mSkip464Xlat, other.mSkip464Xlat) && Objects.equals(mAlwaysOn, other.mAlwaysOn) - && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask); + && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask) + && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning); } /** @@ -1378,7 +1398,9 @@ public class ApnSetting implements Parcelable { && Objects.equals(this.mCarrierId, other.mCarrierId) && Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat) && Objects.equals(this.mAlwaysOn, other.mAlwaysOn) - && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask); + && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask) + && Objects.equals(this.mEsimBootstrapProvisioning, + other.mEsimBootstrapProvisioning); } // Equal or one is null. @@ -1451,6 +1473,7 @@ public class ApnSetting implements Parcelable { apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat); apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn); apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask); + apnValue.put(Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning); return apnValue; } @@ -1724,6 +1747,7 @@ public class ApnSetting implements Parcelable { dest.writeInt(mSkip464Xlat); dest.writeBoolean(mAlwaysOn); dest.writeInt(mInfrastructureBitmask); + dest.writeBoolean(mEsimBootstrapProvisioning); } private static ApnSetting readFromParcel(Parcel in) { @@ -1760,6 +1784,7 @@ public class ApnSetting implements Parcelable { .setSkip464Xlat(in.readInt()) .setAlwaysOn(in.readBoolean()) .setInfrastructureBitmask(in.readInt()) + .setEsimBootstrapProvisioning(in.readBoolean()) .buildWithoutCheck(); } @@ -1842,6 +1867,7 @@ public class ApnSetting implements Parcelable { private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT; private boolean mAlwaysOn; private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR; + private boolean mEsimBootstrapProvisioning; /** * Default constructor for Builder. @@ -2280,6 +2306,19 @@ public class ApnSetting implements Parcelable { } /** + * Sets esim bootstrap provisioning flag + * + * @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap + * provisioning, {@code false} otherwise. + * @hide + */ + @NonNull + public Builder setEsimBootstrapProvisioning(boolean esimBootstrapProvisioning) { + this.mEsimBootstrapProvisioning = esimBootstrapProvisioning; + return this; + } + + /** * Builds {@link ApnSetting} from this builder. * * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)} diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index f4f2be642e81..3d49d81a0f5a 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -166,22 +166,6 @@ android_test { } android_test { - name: "FlickerTestsAppLaunch", - defaults: ["FlickerTestsDefault"], - additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"], - package_name: "com.android.server.wm.flicker.launch", - instrumentation_target_package: "com.android.server.wm.flicker.launch", - srcs: [ - ":FlickerTestsBase-src", - ":FlickerTestsAppLaunchCommon-src", - ":FlickerTestsAppLaunch2-src", - ], - exclude_srcs: [ - ":FlickerTestsActivityEmbedding-src", - ], -} - -android_test { name: "FlickerTestsAppLaunch1", defaults: ["FlickerTestsDefault"], additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"], diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt index 2e9620bb13c5..17f749079da6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt @@ -24,6 +24,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper +import com.android.server.wm.flicker.replacesLayer import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -119,7 +120,13 @@ class OpenTransferSplashscreenAppFromLauncherTransition(flicker: LegacyFlickerTe @FlakyTest(bugId = 240916028) @Test override fun appLayerReplacesLauncher() { - super.appLayerReplacesLauncher() + flicker.replacesLayer( + ComponentNameMatcher.LAUNCHER, + testApp, + ignoreEntriesWithRotationLayer = true, + ignoreSnapshot = true, + ignoreSplashscreen = false + ) } @FlakyTest(bugId = 240916028) diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt index a20266a9b140..28eab8f62e74 100644 --- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt @@ -20,7 +20,6 @@ import com.android.tools.lint.client.api.IssueRegistry import com.android.tools.lint.client.api.Vendor import com.android.tools.lint.detector.api.CURRENT_API import com.google.android.lint.aidl.EnforcePermissionDetector -import com.google.android.lint.aidl.EnforcePermissionHelperDetector import com.google.android.lint.aidl.SimpleManualPermissionEnforcementDetector import com.google.auto.service.AutoService @@ -30,7 +29,8 @@ class AndroidGlobalIssueRegistry : IssueRegistry() { override val issues = listOf( EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION, EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION, - EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER, + EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER, + EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION, SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT, ) @@ -45,4 +45,4 @@ class AndroidGlobalIssueRegistry : IssueRegistry() { feedbackUrl = "http://b/issues/new?component=315013", contact = "repsonsible-apis@google.com" ) -}
\ No newline at end of file +} diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt index 3a95df9b2773..dcd94f1bcba4 100644 --- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt +++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt @@ -30,31 +30,34 @@ import com.android.tools.lint.detector.api.JavaContext import com.android.tools.lint.detector.api.Scope import com.android.tools.lint.detector.api.Severity import com.android.tools.lint.detector.api.SourceCodeScanner +import com.google.android.lint.findCallExpression import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiArrayInitializerMemberValue import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod -import org.jetbrains.uast.UAnnotation +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UDeclarationsExpression import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression import org.jetbrains.uast.UMethod -import org.jetbrains.uast.toUElement +import org.jetbrains.uast.skipParenthesizedExprDown import java.util.EnumSet /** - * Lint Detector that ensures that any method overriding a method annotated - * with @EnforcePermission is also annotated with the exact same annotation. - * The intent is to surface the effective permission checks to the service - * implementations. + * Lint Detector that ensures consistency when using the @EnforcePermission + * annotation. Multiple verifications are implemented: * - * This is done with 2 mechanisms: * 1. Visit any annotation usage, to ensure that any derived class will have - * the correct annotation on each methods. This is for the top to bottom - * propagation. - * 2. Visit any annotation, to ensure that if a method is annotated, it has + * the correct annotation on each methods. Even if the subclass does not + * have the annotation, visitAnnotationUsage will be called which allows us + * to capture the issue. + * 2. Visit any method, to ensure that if a method is annotated, it has * its ancestor also annotated. This is to avoid having an annotation on a * Java method without the corresponding annotation on the AIDL interface. + * 3. When annotated, ensures that the first instruction is to call the helper + * method (or the parent helper). */ class EnforcePermissionDetector : Detector(), SourceCodeScanner { @@ -62,9 +65,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { return listOf(ANNOTATION_ENFORCE_PERMISSION) } - override fun getApplicableUastTypes(): List<Class<out UElement>> { - return listOf(UAnnotation::class.java) - } + override fun getApplicableUastTypes(): List<Class<out UElement?>> = + listOf(UMethod::class.java) private fun annotationValueGetChildren(elem: PsiElement): Array<PsiElement> { if (elem is PsiArrayInitializerMemberValue) @@ -129,11 +131,6 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { overriddenMethod: PsiMethod, checkEquivalence: Boolean = true ) { - // If method is not from a Stub subclass, this method shouldn't use @EP at all. - // This is handled by EnforcePermissionHelperDetector. - if (!isContainedInSubclassOfStub(context, overridingMethod.toUElement() as? UMethod)) { - return - } val overridingAnnotation = overridingMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION) val overriddenAnnotation = overriddenMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION) val location = context.getLocation(element) @@ -169,40 +166,102 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { ) { if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE && annotationInfo.origin == AnnotationOrigin.METHOD) { + /* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */ + val uMethod = element as? UMethod ?: return + if (!isContainedInSubclassOfStub(context, uMethod)) { + return + } val overridingMethod = element.sourcePsi as PsiMethod val overriddenMethod = usageInfo.referenced as PsiMethod compareMethods(context, element, overridingMethod, overriddenMethod) } } - override fun createUastHandler(context: JavaContext): UElementHandler { - return object : UElementHandler() { - override fun visitAnnotation(node: UAnnotation) { - if (node.qualifiedName != ANNOTATION_ENFORCE_PERMISSION) { - return - } - val method = node.uastParent as? UMethod ?: return - val overridingMethod = method as PsiMethod - val parents = overridingMethod.findSuperMethods() - for (overriddenMethod in parents) { - // The equivalence check can be skipped, if both methods are - // annotated, it will be verified by visitAnnotationUsage. - compareMethods(context, method, overridingMethod, - overriddenMethod, checkEquivalence = false) - } + override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context) + + private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() { + override fun visitMethod(node: UMethod) { + if (context.evaluator.isAbstract(node)) return + if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return + + if (!isContainedInSubclassOfStub(context, node)) { + context.report( + ISSUE_MISUSING_ENFORCE_PERMISSION, + node, + context.getLocation(node), + "The class of ${node.name} does not inherit from an AIDL generated Stub class" + ) + return + } + + /* Check that we are connected to the super class */ + val overridingMethod = node as PsiMethod + val parents = overridingMethod.findSuperMethods() + for (overriddenMethod in parents) { + // The equivalence check can be skipped, if both methods are + // annotated, it will be verified by visitAnnotationUsage. + compareMethods(context, node, overridingMethod, + overriddenMethod, checkEquivalence = false) + } + + /* Check that the helper is called as a first instruction */ + val targetExpression = getHelperMethodCallSourceString(node) + val message = + "Method must start with $targetExpression or super.${node.name}(), if applicable" + + val firstExpression = (node.uastBody as? UBlockExpression) + ?.expressions?.firstOrNull() + + if (firstExpression == null) { + context.report( + ISSUE_ENFORCE_PERMISSION_HELPER, + context.getLocation(node), + message, + ) + return + } + + val firstExpressionSource = firstExpression.skipParenthesizedExprDown() + .asSourceString() + .filterNot(Char::isWhitespace) + + if (firstExpressionSource != targetExpression && + firstExpressionSource != "super.$targetExpression") { + // calling super.<methodName>() is also legal + val directSuper = context.evaluator.getSuperMethod(node) + val firstCall = findCallExpression(firstExpression)?.resolve() + if (directSuper != null && firstCall == directSuper) return + + val locationTarget = getLocationTarget(firstExpression) + val expressionLocation = context.getLocation(locationTarget) + + context.report( + ISSUE_ENFORCE_PERMISSION_HELPER, + context.getLocation(node), + message, + getHelperMethodFix(node, expressionLocation), + ) } } } companion object { + + private const val HELPER_SUFFIX = "_enforcePermission" + val EXPLANATION = """ - The @EnforcePermission annotation is used to indicate that the underlying binder code - has already verified the caller's permissions before calling the appropriate method. The - verification code is usually generated by the AIDL compiler, which also takes care of - annotating the generated Java code. + The @EnforcePermission annotation is used to delegate the verification of the caller's + permissions to a generated AIDL method. In order to surface that information to platform developers, the same annotation must be used on the implementation class or methods. + + The @EnforcePermission annotation can only be used on methods whose class extends from + the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the + AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX. + + yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can + either call it directly, or call it indirectly via super.yourMethodName(). """ val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create( @@ -230,5 +289,44 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) ) ) + + val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create( + id = "MissingEnforcePermissionHelper", + briefDescription = """Missing permission-enforcing method call in AIDL method + |annotated with @EnforcePermission""".trimMargin(), + explanation = EXPLANATION, + category = Category.SECURITY, + priority = 6, + severity = Severity.ERROR, + implementation = Implementation( + EnforcePermissionDetector::class.java, + EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) + ) + ) + + val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create( + id = "MisusingEnforcePermissionAnnotation", + briefDescription = "@EnforcePermission cannot be used here", + explanation = EXPLANATION, + category = Category.SECURITY, + priority = 6, + severity = Severity.ERROR, + implementation = Implementation( + EnforcePermissionDetector::class.java, + EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) + ) + ) + + /** + * handles an edge case with UDeclarationsExpression, where sourcePsi is null, + * resulting in an incorrect Location if used directly + */ + private fun getLocationTarget(firstExpression: UExpression): PsiElement? { + if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi + if (firstExpression is UDeclarationsExpression) { + return firstExpression.declarations.firstOrNull()?.sourcePsi + } + return null + } } } diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt deleted file mode 100644 index 758de4dfccf8..000000000000 --- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2022 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.google.android.lint.aidl - -import com.android.tools.lint.client.api.UElementHandler -import com.android.tools.lint.detector.api.Category -import com.android.tools.lint.detector.api.Detector -import com.android.tools.lint.detector.api.Implementation -import com.android.tools.lint.detector.api.Issue -import com.android.tools.lint.detector.api.JavaContext -import com.android.tools.lint.detector.api.Scope -import com.android.tools.lint.detector.api.Severity -import com.android.tools.lint.detector.api.SourceCodeScanner -import com.google.android.lint.findCallExpression -import com.intellij.psi.PsiElement -import org.jetbrains.uast.UBlockExpression -import org.jetbrains.uast.UDeclarationsExpression -import org.jetbrains.uast.UElement -import org.jetbrains.uast.UExpression -import org.jetbrains.uast.UMethod -import org.jetbrains.uast.skipParenthesizedExprDown - -import java.util.EnumSet - -class EnforcePermissionHelperDetector : Detector(), SourceCodeScanner { - override fun getApplicableUastTypes(): List<Class<out UElement?>> = - listOf(UMethod::class.java) - - override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context) - - private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() { - override fun visitMethod(node: UMethod) { - if (context.evaluator.isAbstract(node)) return - if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return - - if (!isContainedInSubclassOfStub(context, node)) { - context.report( - ISSUE_MISUSING_ENFORCE_PERMISSION, - node, - context.getLocation(node), - "The class of ${node.name} does not inherit from an AIDL generated Stub class" - ) - return - } - - val targetExpression = getHelperMethodCallSourceString(node) - val message = - "Method must start with $targetExpression or super.${node.name}(), if applicable" - - val firstExpression = (node.uastBody as? UBlockExpression) - ?.expressions?.firstOrNull() - - if (firstExpression == null) { - context.report( - ISSUE_ENFORCE_PERMISSION_HELPER, - context.getLocation(node), - message, - ) - return - } - - val firstExpressionSource = firstExpression.skipParenthesizedExprDown() - .asSourceString() - .filterNot(Char::isWhitespace) - - if (firstExpressionSource != targetExpression && - firstExpressionSource != "super.$targetExpression") { - // calling super.<methodName>() is also legal - val directSuper = context.evaluator.getSuperMethod(node) - val firstCall = findCallExpression(firstExpression)?.resolve() - if (directSuper != null && firstCall == directSuper) return - - val locationTarget = getLocationTarget(firstExpression) - val expressionLocation = context.getLocation(locationTarget) - - context.report( - ISSUE_ENFORCE_PERMISSION_HELPER, - context.getLocation(node), - message, - getHelperMethodFix(node, expressionLocation), - ) - } - } - } - - companion object { - private const val HELPER_SUFFIX = "_enforcePermission" - - private const val EXPLANATION = """ - The @EnforcePermission annotation can only be used on methods whose class extends from - the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the - AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX. - - yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can - either call it directly, or call it indirectly via super.yourMethodName(). - """ - - val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create( - id = "MissingEnforcePermissionHelper", - briefDescription = """Missing permission-enforcing method call in AIDL method - |annotated with @EnforcePermission""".trimMargin(), - explanation = EXPLANATION, - category = Category.SECURITY, - priority = 6, - severity = Severity.ERROR, - implementation = Implementation( - EnforcePermissionHelperDetector::class.java, - EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) - ) - ) - - val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create( - id = "MisusingEnforcePermissionAnnotation", - briefDescription = "@EnforcePermission cannot be used here", - explanation = EXPLANATION, - category = Category.SECURITY, - priority = 6, - severity = Severity.ERROR, - implementation = Implementation( - EnforcePermissionHelperDetector::class.java, - EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) - ) - ) - - /** - * handles an edge case with UDeclarationsExpression, where sourcePsi is null, - * resulting in an incorrect Location if used directly - */ - private fun getLocationTarget(firstExpression: UExpression): PsiElement? { - if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi - if (firstExpression is UDeclarationsExpression) { - return firstExpression.declarations.firstOrNull()?.sourcePsi - } - return null - } - } -} diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt index 5a63bb4084d2..3ef02f865355 100644 --- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt +++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt @@ -25,10 +25,10 @@ import com.android.tools.lint.detector.api.Issue @Suppress("UnstableApiUsage") class EnforcePermissionHelperDetectorCodegenTest : LintDetectorTest() { - override fun getDetector(): Detector = EnforcePermissionHelperDetector() + override fun getDetector(): Detector = EnforcePermissionDetector() override fun getIssues(): List<Issue> = listOf( - EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER + EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER ) override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt index 10a6e1da91dc..64e2bfbad7bb 100644 --- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt +++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt @@ -20,10 +20,10 @@ import com.android.tools.lint.checks.infrastructure.LintDetectorTest import com.android.tools.lint.checks.infrastructure.TestLintTask class EnforcePermissionHelperDetectorTest : LintDetectorTest() { - override fun getDetector() = EnforcePermissionHelperDetector() + override fun getDetector() = EnforcePermissionDetector() override fun getIssues() = listOf( - EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER, - EnforcePermissionHelperDetector.ISSUE_MISUSING_ENFORCE_PERMISSION + EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER, + EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION ) override fun lint(): TestLintTask = super.lint().allowMissingSdk() |